AODV代码实现详解——原理与源码分析(一)

首先来几个标准参考:
RFC 3561
RFC 3561 中文翻译
一个博客
挺好的另一个博客
事件?
字段长度?

事件驱动
各种定时器
状态转移图?

AODV协议

基本概念

AODV(Ad hoc On-Demand Distance Vector)是一种基于距离向量路由算法的自适应路由协议,用于无线自组织网络(MANET)中的路由选择。AODV 协议的实现流程如下:

  1. 节点广播 RREQ(Route Request)消息:当节点需要发送数据包到某个目的地时,它会广播一个 RREQ 消息来查找到达目的地的路由。这个 RREQ 消息包含源节点地址、目的节点地址和一个唯一的序列号,以防止消息循环。

  2. 节点响应 RREQ 消息:当一个节点收到 RREQ 消息时,它会检查自己的路由表,如果没有到达目的地的路由,则向其邻居节点转发 RREQ 消息。如果节点已经有到达目的地的路由,则向源节点发送 RREP(Route Reply)消息。

  3. 节点广播 RREP 消息:当目的节点收到 RREQ 消息时,它会向源节点发送 RREP 消息,其中包含到达源节点的最短路径。每个节点在转发 RREP 消息之前都会将其缓存起来,以便以后使用。

  4. 节点维护路由表:每个节点都会维护一个路由表,用于存储到达其他节点的路由信息。每当一个节点收到一个 RREP 消息时,它会更新自己的路由表并向其邻居节点广播更新的路由信息。

  5. 节点周期性发送 HELLO 消息:节点会周期性地发送 HELLO 消息以检测邻居节点是否仍然存在。如果一个节点连续几次未收到邻居节点的 HELLO 消息,则认为该节点已经离线,并更新其路由表。

AODV 协议的主要优点是其适应性和效率。由于 AODV 协议仅在需要时才会建立路由,因此它可以有效地减少网络中的控制流量。此外,AODV 协议具有自适应性,可以根据网络拓扑和流量负载动态调整路由。

AODV 协议的核心思想是通过按需路由方式来减少网络中的路由开销。当源节点需要向目的节点发送数据时,它会向周围节点发出路由请求(RREQ)。如果某个节点知道如何到达目的节点,它会向源节点发出路由响应(RREP)。源节点将使用路由响应中的信息来向目的节点发送数据。在这个过程中,每个节点会维护一张路由表,记录到达目的节点的最短路径和下一跳节点。

AODV 协议的一些实现细节:
image.png|400

  1. 路由请求(RREQ):
    当源节点需要向目的节点发送数据时,它会向周围节点发出路由请求(RREQ)。每个节点将检查自己的路由表来查找到达目的节点的最短路径。如果找到了,节点将向源节点发送路由响应,否则节点将广播路由请求。

路由请求包括 RREQ ID、目的 ID、目的序列号、源 ID、源序列号、跳数和洪泛标识。洪泛标识用于避免重复转发路由请求,每个节点只能转发一次具有相同 RREQ ID 的路由请求。

  1. 路由响应(RREP):
    如果某个节点知道如何到达目的节点,它会向源节点发出路由响应(RREP)。路由响应包含源节点的地址、目的节点的地址、目的节点的序列号和到达目的节点的最短路径。源节点将使用路由响应中的信息来向目的节点发送数据。

路由响应包括目的 ID、目的序列号、源 ID、序列号、跳数和路径。路径描述了到达目的节点的最短路径,包括每个节点的地址和序列号。

  1. 路由维护:
    每个节点都会维护一张路由表,记录到达目的节点的最短路径和下一跳节点。节点还会周期性地发送心跳(Hello)消息来保持与周围节点的连接。如果某个节点无法与下一跳节点通信,它会向源节点发送路由错误(RERR)消息,通知源节点重新计算路径。

  2. 序列号:
    为了防止路由环路和路由不稳定,AODV 协议使用序列号来标识每个节点和每个路由。每个节点都具有唯一的序列号,每个路由的序列号递增。当节点收到路由响应时,它将比较目的节点的序列号和自己的路由表中的序列号,如果目的节点的序列号更大,则更新路由表中的信息。

如何解决环路问题?

AODV协议使用了两种方法来避免路由环路的问题,分别是序列号和反向路径检查。

  1. 序列号:
    AODV协议引入了序列号的概念,用于标识每个节点和每个路由。每个节点都具有唯一的序列号,每个路由的序列号递增。当一个节点收到路由请求或路由响应时,它会比较消息中的序列号和自己的路由表中的序列号。如果消息中的序列号较大,则说明该消息是更新的,节点会更新自己的路由表;如果消息中的序列号较小,则说明该消息已经过期,节点会丢弃该消息。

  2. 反向路径检查:
    当一个节点转发路由请求时,它会将自己的地址添加到请求的路由记录中。当一个节点收到路由响应时,它会检查响应中的路由记录,以确定响应是否经过了自己。如果响应经过了自己,则说明存在路由环路,节点会丢弃该响应。

通过使用反向路径检查,AODV协议可以及时发现路由环路,避免数据包在环路中循环。另外,如果某个节点收到了一个来自下一跳节点的数据包,但是该数据包不是该节点向下一跳节点发送的,则说明存在路由环路,该节点会向源节点发送路由错误消息,通知源节点重新计算路径。

代码整体框架

如此长的代码,必须提纲挈领地先了解基本架构,再进行具体地逐函数的了解

主要功能

  1. 报文类的定义(RREQ、RREP、RERR等),用于封装和解析AODV控制报文。

  2. 路由表和寻路队列的管理,用于维护和查找路由信息。

  3. 定时器的实现,用于路由发现、路由维护等定时功能。

  4. 控制报文(如RREQ、RREP、RERR等)的创建、转发和处理。

  5. Hello报文的生成和处理,用于邻居发现和链路检测。

  6. 本地链路管理,用于检测和响应链路断开。

  7. 数据包转发和路由错误处理。

  8. 事件驱动的架构,通过事件队列实现异步处理。

  9. 参数配置,支持启用不同的机制如本地链路修复、扩展环搜索等。

  10. 统计信息收集,用于计算延迟、跳数等性能指标。

  11. 模块接口定义,与外部协议交互。

主要函数与数据结构大致信息

1. 整体理解

报文类有:BaseMessage、RREQ、RREP、RERR、MyBusiness这些类实现了各种AODV控制报文和业务报文的封装,包含报文的定义、解析和组装功能。具体而言:

  • BaseMessage是基类,定义了报文的公共字段如类型、源地址、目的地址等。
  • RREQ类封装了路由请求报文,包含跳数、请求id等AODV特有字段。
  • RREP类封装了路由回复报文。
  • RERR类封装了路由错误报文。
  • MyBusiness类封装了业务数据报文。
    以上这些类通过操作报文字段实现了报文的组装和解析功能。

2. 事件类 MyEvent

封装了一个事件,包含事件处理函数、触发时间、数据等。主要用于事件驱动框架。

// 抽象事件的构造函数
MyEvent::MyEvent(event_func_t f, void* d) :
    time(-1), handler(f), data(d), id(-1) {}

bool MyEvent::operator<(const MyEvent& t) const
{
    if (this->time < t.time) {
        return true;
    }
    return t.time == this->time && this->id < t.id;
}

bool MyEvent::operator==(const MyEvent& t)const
{
    return t.time == this->time && t.handler == this->handler && t.data == this->data;
}

定义了一个名为MyEvent的类,该类表示一个抽象事件。该类包含一个构造函数和两个运算符重载。
构造函数MyEvent接受两个参数:一个是函数指针,表示事件d 处理函数;另一个是void指针,可以存储事件处理函数需要的数据。构造函数还初始化了类的一些成员变量,包括时间戳、事件处理函数、事件数据和事件ID。

运算符重载了小于号(<)和等于号(==)。其中,小于号根据事件的时间戳和ID比较事件的先后顺序,用于在事件队列中排序;等于号判断两个事件对象是否相等,如果它们的时间戳、事件处理函数和事件数据都相等,则认为它们相等。这两个是利用了C++面向对象的重载特性,方便了后续对事件队列的操作。

3. 路由表类rt_table_t

保存一条路由表项,包含目的地址、下一跳、跳数等信息。

rt_table_t::rt_table_t(int dest_addr, int next, int hops, unsigned int seqno,
    bool _state, int _flags) :
    dest(dest_addr), nx_hop(next), hcnt(hops), dest_seq(seqno),
    //lifetime(-1), 
    state(_state), flags(_flags),
    rt_timer_timeout(nullptr, nullptr),
    hello_timer_timeout(nullptr, nullptr) {}
  • dest:目的地址
  • nx_hop:下一跳地址
  • hcnt:跳数
  • dest_seq:目的地址的序列号
  • state:状态标志
  • flags:路由表项的标志
  • rt_timer_timeout:指向路由表定时器超时处理函数的函数指针和函数参数的指针
  • hello_timer_timeout:指向hello定时器超时处理函数的函数指针和函数参数的指针

4. 寻路队列类seek_list_t

保存一个寻路请求相关信息,包含目的地址、序列号、寻路计时器等。

5. 事件队列相关函数

  • eq_insert/eq_erase/eq_set_time/eq_reset_time 用于向事件队列中添加/删除/设置/重置事件。
  • eq_pop 执行队首事件的处理函数;eq_front_time() 返回队首事件的发生时间
  • msg_delete 用于释放报文占用的内存。
  • AodvOutputBuf_push/OutputBuf_push/clear 向发送/接收缓冲区添加业务;或者清空缓冲区。
  • packet_queue_clear/ packet_queue_add 数据包发送缓存队列,清空或者添加消息。packet_queue_set_verdict 对队列里所有包的处理工作,因为当某些包可以发送的时候还是要及时把它发出去的。这个函数比较重要,需要仔细看看

packet_queue_set_verdict

函数设置一个对队列里所有包的处理工作。接受三个参数:

  • dest:目标地址
  • verdict:处理结果,可以是PQ_SENDPQ_DROP,分别表示发送或丢弃该数据包
  • curtime:当前时间,用于计算数据包在队列中的等待时间
    局部变量,包括:
  • count:计数器,用于记录处理了多少个数据包
  • pkt_queue_size:队列长度,用于遍历整个队列
  • rt:指向目标地址对应的路由表项的指针,如果没有对应的路由表项则为nullptr
    函数的主要功能是遍历一个数据包队列,将其中等待目标地址的数据包进行处理,如为他们查找路由等。具体实现如下:
    首先判断如果要发送数据包却没有对应的路由表项,则返回-1表示异常。

然后循环遍历整个数据包队列,从队列头开始处理每个数据包。对于每个数据包,首先判断它在队列中等待的时间是否超过了最大等待时间,如果超过了则释放该数据包的内存,并继续处理下一个数据包。

如果这个数据包不是等待目标地址的数据包,则将其放回队列中,并继续处理下一个数据包。

如果这个数据包是等待目标地址的数据包,且处理结果为丢弃,则释放该数据包的内存,并继续处理下一个数据包。

如果这个数据包是等待目标地址的数据包,且处理结果为发送,则根据目标地址查找路由表,找到下一跳地址,并设置该数据包的源地址为本节点ID,下一跳地址为路由表中找到的地址。如果该数据包是最后一个数据包(即没有下一跳地址),则更新本节点的最后转发数据包时间。如果没有使用二层协议,则释放该数据包的内存;如果使用了二层协议,则将该数据包放入输出缓冲区中。

最后,如果目标地址对应的路由表项存在且有效,并且处理结果为发送,则更新该路由表项和下一跳路由表项的超时时间。
函数返回值是处理了多少个数据包。

6. 定时器相关函数

route_discovery_timeout

处理路由发现请求超时的定时器的回调函数。实际上是实现了协议的部分功能,有些定时器与状态机混用的隐患。

函数输入参数:

  • arg:指向seek_list_t类型结构的指针,该结构记录了路由请求的相关信息
  • curtime:当前时间

函数的主要功能是处理路由请求的超时情况。如果路由请求还未达到最大重试次数,则重新发送路由请求,并更新超时时间。如果路由请求达到了最大重试次数,则表示无法找到目标节点的路由,从seek_list中删除该请求,并尝试使用本地修复机制进行修复。

局部变量:

  • seek_entry:指向seek_list_t类型结构的指针,该结构记录了路由请求的相关信息
  • rt:指向目标节点的路由表项的指针
  • repair_rt:指向要修复的路由表项的指针
  • flooding_flage:泛洪标志,用于区分路由请求和路由响应消息
  • ttl:路由请求消息的TTL值

首先判断seek_entry是否为空,如果为空则直接返回。然后从seek_entry中获取TTL值和重试次数,如果重试次数还未达到最大值,则根据不同的情况更新TTL和超时时间,并重新发送路由请求消息。如果重试次数已经达到最大值,则无法找到目标节点的路由,从seek_list中删除该请求,并尝试使用本地修复机制进行修复。

在重新发送路由请求消息之前,如果目标节点的路由表项存在且其删除时间与当前时间的差小于两倍的网络遍历时间,则更新该路由表项的删除时间。

最后,如果seek_entry已经达到最大重试次数,则从seek_list中删除该请求,并尝试使用本地修复机制进行修复。在尝试本地修复之前,先查找目标节点的路由表项,如果存在并且其状态为需要修复,则调用local_repair_timeout函数进行本地修复。

route_expire_timeout

处理路由表项过期的定时器的回调函数。

  • arg:指向rt_table_t类型结构的指针,该结构记录了路由表项的信息
  • curtime:当前时间

函数的主要功能是处理路由表项的过期情况。如果该路由表项只有一个相邻节点,并且没有接收到该节点的Hello消息,则认为该相邻节点已经离开,需要进行链路断开处理;否则,将该路由表项标记为无效,并清空其前驱节点列表。

首先判断路由表是否为空,如果为空则直接返回。接着判断该路由表项的相邻节点个数hcnt是否为1,如果是,则说明该路由表项只有一个相邻节点,并且没有接收到该节点的Hello消息,即该相邻节点已经离开,需要进行链路断开处理。在链路断开处理中,调用neighbo_link_break函数来删除该相邻节点,同时更新该节点的前驱节点列表。

如果hcnt不为1,则将该路由表项标记为无效,并清空其前驱节点列表。在标记为无效时,调用rt_table_invalidate函数来设置路由表项的状态为无效,并更新其超时时间;在清空前驱节点列表时,调用precursor_list_destroy函数来删除前驱节点列表中的所有节点。

route_delete_timeout 路由删除定时器

如果路由表项指针 rt 为空,则直接返回。
如果路由表项指针 rt 不为空,则调用函数 rt_table_delete(rt) 删除该路由表项。
通过定时器函数删除过期的路由表项,以保证路由表中只保存最新的可用路由信息。

local_repair_timeout 本地修复定时器函数

  1. 指向路由表项的指针 rt,指向 RERR(Route Error)数据包的指针 rerr。
  2. 如果路由表项指针 rt 为空,则直接返回。
  3. 将 RERR 目的地址 rerr_dest 初始化为广播,将路由表项中的 RT_REPAIR 标志位清零。
  4. 如果路由表项中的前驱节点数量不为 0:
    • 调用函数 rerr_create 创建一个 RERR 数据包,包含目的地址、目的序列号以及当前时间等信息。
    • 如果路由表项中的前驱节点数量为 1,则将 RERR 目的地址设置为该前驱节点地址。
    • 将 RERR 数据包发送。
  5. 调用函数 precursor_list_destroy 销毁路由表项中的前驱节点列表。
  6. 将路由表项中的 rt_timer_timeout.handler 成员函数设置为 route_delete_timeout
  7. 调用函数 rt_table_update_timeout 更新该路由表项的定时器,将其设置为 DELETE_PERIOD(路由删除周期),以便在该周期后删除该路由表项。

发生路由故障时,通过本地修复机制对该路由进行修复,以维护路由表中的正确信息。创建一个 RERR 数据包并发送出去,同时将路由表项设置为待删除状态,并在一段时间后删除该路由。

hello_timeout hello 定时器函数

  1. 如果路由表项指针 rt 为空,则直接返回。
  2. 如果路由表项指针 rt 不为空,并且该路由表项状态为 VALID(即有效状态),则执行以下操作:
    • 如果启用了局部修复机制 local_repair,并且该路由表项的 hop count 值小于等于最大修复 TTL(MAX_REPAIR_TTL),则将该路由表项的标志位设置为 RT_REPAIR,表示该路由表项需要进行修复。
    • 调用函数 neighbo_link_break(rt, curtime),将该路由表项中与邻居节点相关的信息进行处理,将其标记为失效状态。

通过定时器函数检查邻居节点是否还处于活动状态,并对失效的邻居节点进行相应的处理,以维护路由表中的正确信息。

echo_timeout

7. 路由表相关函数

  • rt_table_clear 路由表清空
  • rt_table_insert_gateway
  • rt_table_insert_gateway
  • rt_table_update_gateway 添加新表项
  • rt_table_update_timeout
  • rt_table_update_route_timeouts
  • rt_table_find
  • rt_table_invalidate
  • rt_table_delete

8. 寻路队列相关函数

  • seek_list_insert_gateway/remove/find/clear 用于向寻路队列中添加、删除和查找条目。

9. 控制报文处理

rreq相关

rrep相关

rrer相关

10. Hello报文相关函数

用于生成、转发和处理Hello报文,实现邻居发现和链路检测。

11. 本地链路管理函数

  • neighbor_add 处理接收到的报文,更新邻居表。
  • neighbo_link_break 检测链路断开,生成RERR。

12. 业务报文处理

  • business_process 处理最终到达的业务报文。
  • genBusiness_opt 生成业务报文后的处理。
  • recvMessage 将input中的函数注册成为事件
  • processMessage 报文处理函数。重要!!!!

13. 模块接口函数

  • process 接收外部信息,交给内部处理。
  • getAODVrt_table 输出当前的路由表。
  • subNet_rt_table_insert 在网关处添加子网路由表项。

事件驱动

事件驱动思想的整体代码架构

  1. 当有业务数据需要发送时,首先查找路由表。如果有可用路由,则直接封装报文转发。

  2. 如果没有可用路由,则将数据包缓存,并通过route_discovery发起路由请求RREQ。

  3. RREQ通过网络泛洪,当到达目的地或中间节点有新的可用路由时,会触发响应RREP。

  4. 源节点收到RREP后就获得了路由,取出缓存的数据包并发送。

  5. 整个过程中,需要使用seek_list进行路由请求的发起和监控。

  6. 路由表中的路由都有超时时间,超时后会失效。

  7. Hello报文定期发送用于探测链路状态。

  8. 链路断开时,协议定义了在一定时间内,可以通过local_repair尝试进行局部修复。

  9. 局部修复失败后,通过neighbo_link_break检测到链路断开,发送RERR通知其他节点。其他节点收到RERR会使相关路由失效。

  10. 新进入的节点,通过接收业务报文和Hello报文学习邻居和本地链路信息。不会“主动地”探索网络拓扑。

  11. 业务数据报文通过process和genBusiness_opt函数进行转发。

  12. 模块接口将外部消息转入内部输入队列,内部输出转移出模块。

void AODV::process()

核心函数,用于生成各类型事件。对于每个节点来说,看不到其它节点的事件,只需要关心自己的各种到达业务、控制报文。

void AODV::process(std::vector<std::string>& RT_send, const std::vector<std::string>& RT_recv,
    const std::queue<Business>& BusinessBufferInter, bool isGateway)
{
    // 将数据转移到input
    fromBusinessBufToInput(BusinessBufferInter); // 网关接收到的跨网业务
    fromRTrecvBufToInput(RT_recv);
    // 处理input中的数据
    recvMessage();
    while (!events.empty() && eq_front_time() <= this->timer * SLOT_LEN)
    {
        eq_pop();
    }
    // 将处理过程中生成的数据转移到RT_send
    fromOutputToRTSendBuf(RT_send);
    ++this->timer;
}

“将数据转移到input”部分,是在将数据打包成业务。
recvMessage()是接收
我们再看while中的判断,!events.empty()是为了将事件队列全部轮一遍(这个process()每时隙运行一次),eq_front_time() <= this->timer * SLOT_LEN是为了判断运行总时间是否超过了时隙,如果超过了,就暂停。eq_pop()就是将里面的事件一个一个处理了。
最后fromOutputToRTSendBuf(RT_send)

虽然不是最标准的状态机事件驱动,但是也算是核心的架构,如同汽车的发动机一般,这个while循环一圈一圈的转动,驱动着整个工程的运行。

void AODV::fromBusinessBufToInput()

void AODV::fromBusinessBufToInput(const std::queue<Business>& BusinessBuffer) {
    if (BusinessBuffer.empty())
        return;
    std::queue<Business> tmpBuffer=BusinessBuffer;
    while(tmpBuffer.size())
    {
        Business bs = tmpBuffer.front();
        tmpBuffer.pop();
        int src_msg = bs._sr;
        double recvtime = bs._arrtime;
        double gentime = bs._gentime;
        for (auto dest_msg : bs._dr)
        {
            MyBusiness* mbs = new MyBusiness(INIT_ADDR, INIT_ADDR, gentime, recvtime, dest_msg, src_msg);
            this->input.push(mbs);
        }
    }
}

函数目的:将 BusinessBuffer 中的每个 Business 对象转换为 MyBusiness 对象,并将它们添加到 input 队列中。
首先检查 BusinessBuffer 是否为空。再将 BusinessBuffer 复制到一个临时队列 tmpBuffer 中。然后对tmpBuffer 中的每个 Business 对象 bs,将其源地址 _sr、到达时间 _arrtime 和生成时间 _gentime 分别赋值给变量。接着,对于 bs 中的每个目标地址 _dr,创建一个 MyBusiness 对象 mbs,并将其添加到 input 队列中。
整个函数大量运用了stl库中的操作,这个可以了解下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/94025.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FusionAD:用于自动驾驶预测和规划任务的多模态融合

论文背景 自动驾驶&#xff08;AD&#xff09;任务通常分为感知、预测和规划。在传统范式中&#xff0c;AD中的每个学习模块分别使用自己的主干&#xff0c;独立地学习任务。 以前&#xff0c;基于端到端学习的方法通常基于透视视图相机和激光雷达信息直接输出控制命令或轨迹…

【CSS】轮播图案例开发 ( 基本设置 | 子绝父相 | 浏览器水平居中 | 圆角设置 | 绝对定位居中设置 )

代码示例 : <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Banner 轮播</title><style>/* 取消浏览器或者其它标签的默认的内外边距 */* {margin: 0;padding: 0;}/* 取消列表样式 主要是…

特殊的矩阵与特殊的矩阵关系———实对称、正定、对角、零矩阵

一、特殊的矩阵 1、实对称矩阵 定义&#xff1a;都是实数&#xff0c;且 性质&#xff1a; &#xff08;1&#xff09;可以用特征值来求A的大小 &#xff08;2&#xff09;可以得到A的秩 &#xff08;3&#xff09;必定可以相似对角化 运用&#xff1a; 与实对称矩阵A合同的矩…

C#,《小白学程序》第二课:数组与排序

1 文本格式 /// <summary> /// 《小白学程序》第二课&#xff1a;数组与排序 /// </summary> /// <param name"sender"></param> /// <param name"e"></param> private void button2_Click(object sender, EventArgs …

如何选择合适的损失函数

目录 如何选择合适的损失函数 1、均方误差&#xff0c;二次损失&#xff0c;L2损失&#xff08;Mean Square Error, Quadratic Loss, L2 Loss&#xff09; 2、平均绝对误差&#xff0c;L1损失&#xff08;Mean Absolute Error, L1 Loss&#xff09; 3、MSE vs MAE &#xff…

数据增强:提高机器学习性能的有效技巧

文章目录 数据增强的原理常用的数据增强技术图像数据增强文本数据增强音频数据增强 数据增强的代码示例拓展应用与挑战结论 &#x1f389;欢迎来到AIGC人工智能专栏~数据增强&#xff1a;提高机器学习性能的有效技巧 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&a…

Rabbitmq的Federation Exchange

(broker 北京 ) &#xff0c; (broker 深圳 ) 彼此之间相距甚远&#xff0c;网络延迟是一个不得不面对的问题。有一个在北京的业务(Client 北京 ) 需要连接 (broker 北京 ) &#xff0c;向其中的交换器 exchangeA 发送消息&#xff0c;此时的网络延迟很小&#xff0c;(C…

【网络基础实战之路】基于三层架构实现一个企业内网搭建的实战详解

系列文章传送门&#xff1a; 【网络基础实战之路】设计网络划分的实战详解 【网络基础实战之路】一文弄懂TCP的三次握手与四次断开 【网络基础实战之路】基于MGRE多点协议的实战详解 【网络基础实战之路】基于OSPF协议建立两个MGRE网络的实验详解 【网络基础实战之路】基于…

推荐前 6 名 JavaScript 和 HTML5 游戏引擎

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建3D应用场景 事实是&#xff0c;自从引入JavaScript WebGL API以来&#xff0c;现代浏览器具有直观的功能&#xff0c;使它们能够渲染更复杂和复杂的2D和3D图形&#xff0c;而无需依赖第三方插件。 你可以用纯粹的JavaScript开…

c++冒泡排序的动画演示+程序实现

冒泡排序&#xff08;Bubble Sort&#xff09;是一种计算机科学领域的较简单的排序算法。 它重复地走访过要排序的元素列&#xff0c;依次比较两个相邻的元素&#xff0c;如果顺序&#xff08;如从大到小、首字母从Z到A&#xff09;错误就把他们交换过来。走访元素的工作是重复…

小白到运维工程师自学之路 第七十九集 (基于Jenkins自动打包并部署Tomcat环境)2

紧接上文 4、新建Maven项目 clean package -Dmaven.test.skiptrue 用于构建项目并跳过执行测试 拉到最后选择构建后操作 SSH server webExec command scp 192.168.77.18:/root/.jenkins/workspace/probe/psi-probe-web/target/probe.war /usr/local/tomcat/webapps/ /usr/loca…

代码随想录算法训练营第五十天|LeetCode 739,496

目录 LeetCode 739.每日温度 LeetCode 496.下一个更大元素&#xff01; LeetCode 739.每日温度 文章讲解&#xff1a;代码随想录 力扣题目&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 代码如下&#xff08;Java&#xff09;&#xf…

W6100-EVB-PICO进行UDP组播数据回环测试(九)

前言 上一章我们用我们的开发板作为UDP客户端连接服务器进行数据回环测试&#xff0c;那么本章我们进行UDP组播数据回环测试。 什么是UDP组播&#xff1f; 组播是主机间一对多的通讯模式&#xff0c; 组播是一种允许一个或多个组播源发送同一报文到多个接收者的技术。组播源将…

ubuntu22安装和部署Kettle8.2

前提 kettle是纯java编写的etl开源工具&#xff0c;目前kettle7和kettle8都需要java8或者以上才能正常运行。所以运行kettle前先检查java环境是否正确配置&#xff0c;java版本是否是8或者以上。 kettle安装 1、创建kettle目录&#xff0c;并将kettle的zip包解压到kettle目…

c++的分文件编写

前言 在C中&#xff0c;你可以将代码分割成多个文件来提高可维护性和组织性。分割文件有助于将代码模块化&#xff0c;使大型项目更易于管理。以下是C中关于分文件的一些规则和概念&#xff1a; 理论知识 头文件&#xff08;Header Files&#xff09;&#xff1a; 头文件通常…

多线程(二)

一.关于线程的常用操作 1.启动线程 run(): 对于run方法的覆写只是指定线程要做的任务清单&#xff0c;而不是真正的启动线程 start()&#xff1a; start()方法才是真正的在底层创建出一个线程&#xff0c;并且启动 2.中断线程 1.通过共享的标记来中断 package demo; impor…

交通科技与管理杂志社交通科技与管理编辑部2023年第9期目录

专家论坛 黑龙江省经济高质量发展与生态环境保护耦合协调发展研究 刘降斌;祃玉帅; 1-5142 我国省际数字经济高质量发展水平综合评价研究 耿娟;毕晨曦; 6-8 振兴龙江《交通科技与管理》投稿邮箱&#xff1a;cn7kantougao163.com(注明投稿“《交通科技与管理》”) 数…

代码随想录Day_48打卡

①、打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房…

3、当今的企业架构分析

3、当今的企业架构分析 3、分库分表水平拆分&#xff08;MySQL集群&#xff09; 因为一个数据库装不下了&#xff0c;需要分库分表&#xff0c;读写分离&#xff0c;主从复制&#xff0c;主节点M与从节点s组成了一个数据库的集群&#xff0c;组成了一个小的单元&#xff0c;前端…

Android ---使用Jenkins 打包release版本不能安装或者安装后不显示APP

大家在用 Jenkins的时候&#xff0c;是不是会觉得很爽&#xff0c;因为他在用的过程中&#xff0c;是无脑的&#xff0c;毕竟一键触发&#xff01;&#xff01;&#xff01;&#xff01; 这边记录一个昨天&#xff0c;今天遇到的一个坑货问题&#xff0c;别人提交了所有代码&am…