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

news2024/10/7 12:25:30

首先来几个标准参考:
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库中的操作,这个可以了解下。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter问题记录 - Unable to find bundled Java version

新版本的Android Studio真的移除了JRE&#xff0c;jre目录找不到&#xff0c;怪不得报错了&#xff0c;不过多了一个jbr目录&#xff0c;找了个以前的Android Studio版本对比 搜了一下jbr&#xff08;JetBrains Runtime&#xff09;&#xff0c;原来IDEA老早就开始用了&#xf…

Redis 7 教程 数据持久化

总体 RDB 介绍 RDB 持久化以指定的时间间隔执行数据集的时间点快照 。 把某一时刻的数据和状态以文件的形式写到磁盘上,即使出现故障宕机,快照文件也不会丢失,数据的可靠性得到保证。快照文件就是RDB(Redis DataBase)文件(dump.rdb) 作用 在指定的时间间隔内将内存中的数…

财务数据分析怎么做?看看奥威BI数据可视化工具的解法

从以往的BI智能数据可视化分析项目来看&#xff0c;要想快刀砍乱麻地做好财务数据分析&#xff0c;为企业运营决策提供更加直观深入的数据支持&#xff0c;那就需要为财务数据分析做好数据导入、建模、报表制作、展示等多方面的准备。奥威BI数据可视化工具为此特意打造了一套标…

C#实战:基于腾讯OCR技术实现企业证书识别和数据提取实践

一、OCR技术介绍 在当今数字化时代&#xff0c;OCR&#xff08;Optical Character Recognition&#xff09;识别技术正发挥着越来越重要的作用。OCR技术通过将图像中的文字转化为可编辑的文本形式&#xff0c;实现了对大量纸质文档的数字化处理和信息提取。常见的有企业资质证…

【uniapp】 实现公共弹窗的封装以及调用

图例&#xff1a;红框区域为 “ 内容区域 ” 一、组件 <!-- 弹窗组件 --> <template> <view class"add_popup" v-if"person.isShowPopup"><view class"popup_cont" :style"{width:props.width&&props.width&…

【VLDB 2023】基于预测的云资源弹性伸缩框架MagicScaler,实现“高QoS,低成本”双丰收

开篇 近日&#xff0c;由阿里云计算平台大数据基础工程技术团队主导&#xff0c;与计算平台MaxCompute团队、华东师范大学数据科学与工程学院、达摩院合作&#xff0c;基于预测的云计算平台资源弹性伸缩框架论文《MagicScaler: Uncertainty-aware, Predictive Autoscaling 》被…

解锁市场进入成功:GTM 策略和即用型示例

在最初的几年里&#xff0c;创办一家初创公司可能会充满挑战。根据美国小企业管理局的数据&#xff0c;大约三分之二的新成立企业存活了两年&#xff0c;几乎一半的企业存活了五年以上。导致创业失败的因素有市场需求缺失、资金短缺、团队不合适、成本问题等。由此&#xff0c;…

Flutter可执行屏幕动画的AnimateView

1.让动画使用起来就像使用widget。 2.可自定义动画。 3.内置平移动画。 演示&#xff1a; 代码: import dart:math; import package:flutter/cupertino.dart;class AnimateView extends StatefulWidget {///子Widgetfinal Widget child;///动画自定义final IAnimate? anim…

什么,一条指令直接黑了数据库!

什么&#xff0c;一条指令直接黑了数据库&#xff01; shigen最近研究了一下一款渗透工具sqlMap。它一款流行的开源工具&#xff0c;用于自动化SQL注入攻击和渗透测试。它专门设计用于检测和利用Web应用程序中的SQL注入漏洞。SQLMap具有丰富的功能集&#xff0c;可自动检测和利…

工厂方法模式的概述和使用

目录 一、工厂方法模式概述1. 定义2. 使用动机 二、工厂方法模式结构1. 模式结构2. 时序图 三、工厂方法模式的使用实例四、工厂方法模式的优缺点五、工厂方法模式在Java中应用 原文链接 一、工厂方法模式概述 1. 定义 工厂方法模式(Factory Method Pattern)又称为工厂模式&…

高通面临难题,Oryon核心存在问题,高通8cx Gen 4芯片将推迟发布

"高通公司面临难题&#xff0c;可能会导致骁龙8cx Gen 4的发布时间推迟"&#xff0c;关于骁龙8cx Gen 4处理器&#xff0c;还有一些其他值得关注的特点和功能。首先&#xff0c;据悉&#xff0c;骁龙8cx Gen 4采用了高通自家研发的Oryon核心架构&#xff0c;这是一项…

春秋云镜 Brute4Road

flag1 fscan扫描发现&#xff0c;6379开放ftp可以匿名登录 这里直接尝试了去打redis但是只有主从复制能成功&#xff08;这里应该是靶场有设置吧&#xff0c;对6379操作过后再次操作就会显示端口拒绝访问直接重置就可以了&#xff09; 之后用脚本一把梭哈即可获得shell #更改…

C++ list模拟实现

list模拟实现代码&#xff1a; namespace djx {template<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& x T()):_data(x),_prev(nullptr),_next(nullptr){}};template<class T,class Ref,class Pt…

好用的网页制作工具就是这6个,快点来看!

对于网页设计师来说&#xff0c;好用的网页设计工具是非常重要的&#xff0c;今天本文收集了6个好用的网页设计工具供设计师自由挑选使用。在这6个好用的网页设计工具的帮助下&#xff0c;设计师将获得更高的工作效率和更精致的网页设计效果&#xff0c;接下来&#xff0c;就一…

Android studio APK切换多个摄像头(Camera2)

1.先设置camera的权限 <uses-permission android:name"android.permission.CAMERA" /> 2.布局 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"and…

vue过渡及动画

文章目录 前言类名使用自己定义动画样式多个元素过渡使用第三方库 前言 对于vue中的过渡与动画&#xff0c;官网上是这样概述的&#xff1a; Vue 在插入、更新或者移除 DOM 时&#xff0c;提供多种不同方式的应用过渡效果。包括以下工具&#xff1a; 在 CSS 过渡和动画中自动…

基于空洞卷积DCNN与长短期时间记忆模型LSTM的dcnn-lstm的回归预测模型

周末的时候有时间鼓捣的一个小实践&#xff0c;主要就是做的多因子回归预测的任务&#xff0c;关于时序数据建模和回归预测建模我的专栏和系列博文里面已经有了非常详细的介绍了&#xff0c;这里就不再多加赘述了&#xff0c;这里主要是一个模型融合的实践&#xff0c;这里的数…

避雷!教你正确区分流量卡,不看可别后悔!

分不清真假流量卡&#xff1f; 想要手机流量卡&#xff0c;不小心买到了物联卡&#xff0c;结果被商家割了韭菜&#xff1f; 对于流量卡的套路太多了&#xff1f;你是否还傻傻分不清楚&#xff0c;今天&#xff0c;这篇文章教你正确区分这两种不同类型的卡。 ​ 赶紧收藏&am…

文件重命名与隐藏编号一键搞定!让不同类型的文件整齐有序

大家好&#xff01;在整理和管理不同类型的文件时&#xff0c;我们经常遇到文件名不规范、编号杂乱的情况&#xff0c;使得文件整体显得混乱无序。为了帮助您达到整齐有序的效果&#xff0c;我们自豪地推出了一款高效的工具——文件重命名与隐藏编号软件&#xff01;让您能够轻…

Redis进阶 - JVM进程缓存

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis进阶 - JVM进程缓存 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-advance-jvm-process-cache.html 传统缓存的问题 传统的缓存策略一般是请求到达 Tomcat 后&#xff0c;先查询 Redis &…