首页 / 资讯中心 / 文章详情

AODV协议NS2仿真:三个头文件拆解与避坑指南

AODV协议NS2仿真:三个头文件拆解与避坑指南 ★ FEATURED ARTICLE
简介面向移动自组网Ad Hoc研究与实践的AODV协议核心头文件集适用于通信专业学生、网络仿真开发人员及无线自组网方向的研究者。压缩包共3个文件均为.h头文件整体仅8KB结构轻量便于快速查阅和二次开发。aodv.h定义协议基础结构与路由表条目aodv_packet.h封装RREQ、RREP、RERR三类报文格式与解析逻辑aodv_rtable.h负责路由表存储管理与转发决策三个头文件相互配合完整呈现了AODV按需距离矢量路由协议在代码层面的核心实现。已有243人学习本资源。通过对照头文件中的数据结构与消息定义可深入理解AODV路由发现、路由应答与路由维护机制为在NS2/NS3等仿真平台中搭建车载自组网、应急通信等实验场景提供直接参考也可作为课程设计或毕业设计中实现自组网路由功能的基础起点。1. 移动自组网仿真先从 AODV 三个头文件下手移动自组网Ad Hoc Network仿真绕不开 AODV 路由协议。这个资源是个很小的压缩包里面只有三个头文件aodv.h、aodv_packet.h、aodv_rtable.h。第一次拿到的人多半会愣住——没有 .cc 源文件也没有脚本这能干什么我拆完后的结论是这三个头文件恰恰是 AODV 在 NS2 仿真体系里的“骨架”你在任何教材里看到的 RREQ/RREP/RERR 报文字段、路由表条目、协议 Agent 的对外接口全都浓缩在这三个文件里。对于做车载自组网、应急通信仿真的从业者或研究生它最适合用来干两件事一是快速搞清 AODV 协议的数据结构到底怎么设计二是作为自己实现 AODV 变种比如加 QoS、加能量感知的起点。下面我按协议机制、文件拆解、仿真落地、踩坑记录这条路走一遍。2. AODV 路由协议按需建路的三段式报文机制2.1 RREQ/RREP/RERR 三种报文怎么协作AODV 的全称是 Ad hoc On-Demand Distance Vector Routing核心思想只有一个平时不维护路由等到源节点要发包且没有现成路由时才临时去找路。找路的动作就是广播 RREQRoute Request收到 RREQ 的节点如果自己是目的节点或者手里有足够新的到目的节点的路由就单播回 RREPRoute Reply否则继续广播 RREQ。当链路断开导致某条正在使用的路由失效时上游节点会向所有受影响源节点发送 RERRRoute Error。三个头文件里aodv_packet.h 就是干这个的。它定义了 AODV 报文在 NS2 里怎么封装每种报文类型对应一个结构体。常见做法是这样定义报文类型#define AODV_RREQ 1 #define AODV_RREP 2 #define AODV_RERR 3 struct hdr_aodv_rreq { u_int8_t rreq_type; // 报文类型固定为 AODV_RREQ u_int8_t rreq_hop_count; // 当前跳数每转发一次加 1 u_int32_t rreq_id; // RREQ ID配合源地址唯一标识一个请求 u_int32_t rreq_dst; u_int32_t rreq_dst_seqno; // 目的节点序列号用于判断路由新鲜度 u_int32_t rreq_src; u_int32_t rreq_src_seqno; // 源节点序列号 };这段代码里最关键的是 rreq_id 和 rreq_dst_seqno。rreq_id 用来防止同一个路由请求被无限转发每个节点会记住自己最近转发过的 (源地址, RREQ ID) 组合重复的 RREQ 直接丢弃。dst_seqno 则是 AODV 防环和判断路由新旧的核心后面单独讲。再看 RREP 的结构struct hdr_aodv_rrep { u_int8_t rrep_type; u_int8_t rrep_hop_count; u_int32_t rrep_dst; u_int32_t rrep_dst_seqno; u_int32_t rrep_src; u_int32_t rrep_lifetime; // 路由缓存时间单位是毫秒 };RREP 是单播回给源节点的所以不需要 rreq_id。rrep_lifetime 决定中间节点和源节点把这条反向路由缓存多久过了这个时间路由表项就标记为失效。很多人仿真时发现路由不稳定喜欢调协议参数但真正最先该看的是 lifetime 和下面要说的路由超时之间配不配。RERR 结构会简单一些通常是一个目的节点地址加上该目的节点的失效序列号struct hdr_aodv_rerr { u_int8_t rerr_type; u_int8_t rerr_count; // 包含的不可达目的地址数量 u_int32_t rerr_dst; u_int32_t rerr_dst_seqno; };一个 RERR 可以携带多个不可达目的地址rerr_count 就是用来告诉接收方这个报文里一共报了几个失效目的地。实际仿真里最常见的错误是实现时只报一个导致多跳链路上游节点拿不到完整失效信息路由黑洞就会出现。2.2 距离向量与序列号为什么 AODV 能防环传统距离向量协议比如 RIP靠跳数选路最大问题是收敛慢出现环路后要等路由表慢慢传开才能发现。AODV 在距离向量上加了目的序列号每个目的节点维护一个单调递增的序列号路由表里每一项也保存着自己所知道的目的序列号。节点比较两条到同一目的地的路由时规则很简单谁的序列号大谁就更“新”优先选序列号大的序列号一样时选跳数少的。当一个节点收到 RREQ 或 RREP 时只有发现包里的 dst_seqno 比自己路由表里记的还新才会更新路由表。这样一来过期的反向路由不会被重新启用环路在形成前就被序列号判断挡掉了。源序列号 src_seqno 的作用稍微隐蔽一点。AODV 标准里源节点在发起 RREQ 前会把自己序列号加 1中间节点收到 RREQ 后会利用源序列号建立到源节点的反向路由。反向路由不一定是最短路径但一定是“最新见过这个源节点”的路径。很多新手在这里犯错实现时只更新正向路由忘记在收到 RREQ 时建立或更新反向路由结果 RREP 根本回不到源节点。在 aodv.h 里序列号通常被定义成 u_int32_t这个细节到了仿真时间长的时候会引出大坑32 位序列号溢出后会回绕到 0如果代码里直接用大于号比较新旧回绕后会把旧路由当新路由。标准做法是使用 RFC 1982 的串行号算术比较而不是简单的seq_a seq_b。2.3 头文件里藏着哪些关键结构体把三个文件拆开看职责非常清晰。aodv_packet.h 负责所有报文的封装定义包括三种控制报文和对应的Packet头部访问函数aodv_rtable.h 负责路由表包括路由表项结构、路由表类以及插入、删除、查找、更新等操作aodv.h 则把协议主体串起来定义 AODV Agent 类包含定时器、发报函数、收报处理函数。我自己习惯先把路由表项结构背下来因为它是理解 AODV 一切行为的中枢struct aodv_rt_entry { u_int32_t rt_dst; // 目的地址 u_int32_t rt_seqno; // 目的序列号 u_int8_t rt_hop_count; // 到目的地的跳数 u_int32_t rt_next_hop; // 下一跳地址 double rt_expire; // 路由过期时间 u_int32_t rt_flags; // 路由状态有效/无效/正在修复 };这个结构体在 aodv_rtable.h 里几乎一定能看到。rt_flags 的取值一般有RTF_UP、RTF_DOWN、RTF_IN_REPAIR三种。本地修复Local Repair开启时断链节点会把路由标记为RTF_IN_REPAIR然后自己发起 RREQ 找新路径而不是直接发 RERR。这个机制对端到端延迟影响很大如果你在仿真结果里看到某条流延迟突然飙升后恢复多半是本地修复在起作用。下面用一个表格把三个文件的职责对应到协议机制上方便对照着看文件对应协议机制主要内容aodv_packet.h报文生成与解析RREQ/RREP/RERR 结构体、报文类型常量、packet 头部访问函数aodv_rtable.h路由维护路由表项结构、路由表类、插入/删除/查找、序列号更新aodv.h协议状态机AODV Agent 类、定时器、收发处理、与上层和网络接口的交互3. 拆解 aodv.h / aodv_packet.h / aodv_rtable.h协议主控、报文封装与路由表3.1 aodv_packet.h报文封装与字节序问题在 NS2 里每种协议报文都要绑定到一个 Packet 类型上然后通过Packet::add_header()注册头文件。aodv_packet.h 里通常会有一个hdr_aodv结构体它把所有 AODV 控制报文的公共头通常是一个类型字段和私有数据整合在一起或者用指针分别指向具体的 RREQ/RREP/RERR 结构体。取决于实现方式你看到的可能是这样的联合体struct hdr_aodv { u_int8_t type; union { struct hdr_aodv_rreq rreq; struct hdr_aodv_rrep rrep; struct hdr_aodv_rerr rerr; } u; };定义好结构体后还需要给 NS2 的 Packet 类注册头部偏移量常见做法是这样static int hdr_aodv_offset; static class AODVHeaderClass : public PacketHeaderClass { public: AODVHeaderClass() : PacketHeaderClass(PacketHeader/AODV, sizeof(hdr_aodv)) { bind_offset(hdr_aodv_offset); } } class_aodv_hdr;这段代码的作用是把hdr_aodv的偏移量告诉整个模拟器。之后任何代码里通过HDR_AODV(p)宏就能拿到当前 packet 的 AODV 头部指针。这一步看起来像是纯框架代码但它直接决定了后面所有报文读写是否有效。如果你在移植到 ns-3 时只搬了报文结构体却漏了注册逻辑那模拟器根本识别不了 AODV 报文所有包都会被当成普通数据。字节序问题在这里容易被忽略。NS2 默认在 x86 环境下跑小端字节序但协议标准里报文字段是网络字节序。单机仿真时因为所有节点都是同一台机器上模拟出来的整数所以大小端问题暴露不出来一旦你要把两个不同平台生成的 trace 做对比分析或者把这段代码搬到 ARM 开发板上做半实物仿真就会出现字段解析错乱。我的习惯是在构造报文时统一用htons/htonl处理多字节字段虽然仿真里看不到差异但代码移植到实网设备时你就知道后悔药在哪买了。3.2 aodv_rtable.h路由表项与过期策略路由表是所有距离向量协议的“黑匣子”。AODV 的路由表不需要像 OSPF 那样维护全网拓扑它只保存那些最近通信过的目的节点。aodv_rtable.h 里一般会有一个aodv_rtable类内部用一个哈希表或链表存放aodv_rt_entry并提供以下接口class aodv_rtable { public: aodv_rt_entry* lookup(u_int32_t dst); void insert(u_int32_t dst, u_int32_t seqno, u_int32_t hop_count, u_int32_t next_hop, double expire); void update(aodv_rt_entry* rt, u_int32_t seqno, u_int32_t hop_count, u_int32_t next_hop, double expire); void remove(u_int32_t dst); };lookup 函数是使用频率最高的每次收到数据包、RREP、RERR 都要查表。最常见的翻车点在于 lookup 的返回值没有区分“找到但已过期”和“根本不存在”。很多代码里只判断if (rt)不检查rt-rt_flags结果把一条已经带着 RTF_DOWN 标记的旧路由当有效路由发出去等到下一跳节点收到数据包回头查表时才发现根本没有对应路由导致丢包。过期策略这里AODV 标准给了一个重要参数ACTIVE_ROUTE_TIMEOUT默认 3 秒。也就是说一条路由只要在 3 秒内被用于转发过数据包就算活跃活跃路由可以继续保留。只有在路由长时间不用、超过DELETE_TIMEOUT默认 5 秒后才真正把表项删掉。仿真中如果业务流是间歇式的比如每 10 秒发一个包你会看到路由表项总是建了又删、删了又建路由开销占比特别大。这种时候调小MY_ROUTE_TIMEOUT系数的收益远比增加发送功率效果好。路由表更新时的序列号判断逻辑也应该收敛到update函数里方便统一维护。标准逻辑是if (new_seqno rt-rt_seqno || (new_seqno rt-rt_seqno new_hop_count rt-rt_hop_count)) { rt-rt_seqno new_seqno; rt-rt_hop_count new_hop_count; rt-rt_next_hop next_hop; rt-rt_expire CURRENT_TIME ACTIVE_ROUTE_TIMEOUT; }注意第二行条件跳数变小才更新。如果新路径跳数更大但序列号相同标准做法是保留旧路径。很多简化版本不管这个导致路由在两条等价路径之间抖动仿真 trace 看起来就是无规律的延迟跳变。3.3 aodv.h协议状态机与外部接口aodv.h 定义的是协议主体通常是一个继承自Agent的类名字就叫AODV。核心接口可以归纳成两类一类是给上层比如 UDP调用的发送入口另一类是从通道收到包之后的处理入口。在 NS2 里对应的函数是recv()和对每个报文的 handlerclass AODV : public Agent { public: void recv(Packet* p, Handler* h); void recv_request(Packet* p); void recv_reply(Packet* p); void recv_error(Packet* p); void send_request(u_int32_t dst); void send_reply(u_int32_t dst, u_int32_t src, u_int32_t seqno, u_int32_t hop_count); void send_error(Packet* p); };协议状态机在recv()里展开收到一个包先看是不是 AODV 控制报文是的话按 type 分发给 recv_request / recv_reply / recv_error不是的话就是数据包查路由表有有效下一跳就转发没有就先把数据包缓存起来并发起 RREQ。注意“缓存数据包”这一步非常重要因为从发起 RREQ 到收到 RREP 通常有好几毫秒如果不缓存数据包直接丢吞吐量指标会严重失真。send_request()里有一个限流机制必须实现不然一个目的不可达时源节点会无限重发 RREQ直到把整个仿真场景的队列占满。标准做法是给每个目的地址维护一个 RREQ 重试计数器和等待时间。第一次发 RREQ 后等待RREQ_RETRIES次每次把等待时间翻倍超过最大重试次数就放弃向上层报目的不可达。这部分的实现细节不在头文件里但 aodv.h 会声明重试计数器的成员变量比如u_int32_t rreq_count和u_int32_t rreq_last_sent_time。定时器也是 aodv.h 里的重头戏。AODV 的定时器分两类一类是路由表过期清理定时器周期性扫表删掉到期路由另一类是 RREQ 等待定时器控制重传。如果你在研究 AODV 的延迟性能重点关注第二类。RREQ 等待时间初始值一般取NET_TRAVERSAL_TIME这个值在 NS2 的默认 AODV 实现里是 0.03 秒也就是 30 毫秒。自组网环境下这个值偏乐观实际车载场景中信号遮挡严重30 ms 内回不来很正常很多仿真论文里把NET_TRAVERSAL_TIME调大到 0.1 秒端到端延迟会明显变化但投递率改善也很可观。4. 把 AODV 模块跑进 NS2 / NS3 仿真配置步骤与参数设定4.1 在 NS2 中挂载 AODV 模块的常见做法拿到这三个头文件后要跑仿真还需要配套的 .cc 实现。NS2 自带的 aodv 实现就在ns-2.35/aodv/目录下里面有 aodv.cc、aodv.h、aodv_packet.h、aodv_rtable.h和你手里这份是同一族的东西。如果你手里的头文件来自某本教材或课程设计最稳妥的做法是找到对应的 aodv.cc 补全把它们放到同一个目录下然后修改 NS2 里的Makefile和trace相关文件重新编译。如果下载资源里只有三个头文件没有 .cc我一般会这样做先把 NS2 自带的 aodv.cc 调出来用它作参照把手里头文件里的字段和函数签名逐个对齐。因为自带的 aodv.h 和 aodv_packet.h 与你这三份大概率只是命名和注释不同实质结构非常接近。对齐之后用补全的源码替换自带文件重新编译cd ns-2.35 cp aodv/aodv.h aodv/aodv_packet.h aodv/aodv_rtable.h aodv/ ./configure make clean make -j4这个过程中最容易翻车的就是config.h和tclcl的版本不一致。NS2 是老代码在 GCC 5 以上的环境下编译会有大量报错常见解法是装gcc-4.8或者给 Makefile 加-DUSE_SINGLE_ADDRESS_SPACE。我可不像那些博客里写得那么玄这一步在 2024 年之后基本就是劝退重灾区很多人卡两天就放弃了。我的建议是直接用 Ubuntu 16.04 的 Docker 镜像来编译比自己折腾本机编译器靠谱得多。编译通过后还需要确认新协议被注册到 OTcl 里。NS2 的 Agent 类需要有一个 OTcl 名字通常是Agent/AODV这要在ns-lib.tcl里加一行Agent/AODV instance如果没有这行仿真脚本里new Agent/AODV会直接报找不到类。很多教程忽略这一步让你以为自己代码写错了其实只是没有把协议类注册进 Tcl 解释器。4.2 仿真场景 TCL 脚本节点移动模型与业务流参数跑 AODV 仿真最经典的是车载自组网场景。这里用一个 20 个节点、随机路点移动的脚本来做演示。先设定基本参数set val(chan) Channel/WirelessChannel set val(prop) Propagation/TwoRayGround set val(netif) Phy/WirelessPhy set val(mac) Mac/802_11 set val(ifq) Queue/DropTail/PriQueue set val(ll) LL set val(ant) Antenna/OmniAntenna set val(nn) 20 set val(rp) AODV set val(x) 1500 set val(y) 900 set val(stop) 200这些参数定义了无线信道、传播模型、MAC 层和节点数量。val(rp) AODV就是告诉 NS2 使用我们编译进去的 AODV 路由协议。x和y决定了仿真区域大小区域越大节点越稀疏路由跳数越多AODV 的路由发现开销也越大。移动模型用标准随机路点for {set i 0} {$i $val(nn)} {incr i} { set x_pos [expr {int(rand() * $val(x))}] set y_pos [expr {int(rand() * $val(y))}] $node_($i) set X_ $x_pos $node_($i) set Y_ $y_pos $node_($i) set Z_ 0.0 $ns_ at [expr {rand() * 10}] $node_($i) setdest [expr {rand() * $val(x)}] [expr {rand() * $val(y)}] [expr {5 rand() * 25}] }这里每个节点在 0 到 10 秒之间开始移动目标点随机速度随机在 5 到 30 m/s 之间。速度参数对 AODV 影响非常直接速度越快链路断裂越频繁RREQ/RERR 消息占比越高。你要是想复现“高速车联网下 AODV 性能下降”的现象就把速度区间调到 15 到 30 m/s这个区间最容易看到路由失效风暴。业务流用 CBR 发包4 条并发流set udp0 [new Agent/UDP] set cbr0 [new Application/Traffic/CBR] $udp0 set packetSize_ 512 $cbr0 set interval_ 0.1 $cbr0 attach-agent $udp0 $ns_ attach-agent $node_(0) $udp0 $ns_ at 10.0 $cbr0 start $ns_ at 190.0 $cbr0 stopinterval_ 0.1表示每秒发 10 个包每个包 512 字节码率大概 40 kbps。这个负载对 AODV 来说很轻能够明显看出路由建立阶段的开销。如果你想压测协议把 interval 改成 0.02也就是每秒 50 包此时路由表查找和队列管理的压力会变大AODV 的性能瓶颈会从路由发现转移到队列丢弃上。4.3 数据采集延迟、吞吐量与路由开销怎么算NS2 跑完会生成一个 trace 文件常见命名是out.tr。分析 AODV 性能最常用的三个指标是端到端延迟、投递率、路由开销。端到端延迟用下面这条命令从 trace 里提取awk {if($1r $4AGT $7cbr) print $2, $10} out.tr received.txt这条命令把 AGT 层收到 cbr 包的事件抓出来输出时间点和包 ID。之后再和发送时刻对比计算延迟。这里的$2是事件时间$10是包 ID$4AGT表示应用层接收事件排除了 MAC 层重传和路由层转发干扰。吞吐量则统计在一段时间内收到的字节数awk {if($1r $4AGT $7cbr){start$2; bytes$9; }} END{print bytes/(end-start)} out.tr注意$9是包大小字段不同 NS2 版本里字段位置可能有偏移我用的版本是 2.35如果换成 ns-3 就需要完全不同的解析思路。路由开销的统计稍微麻烦些。AODV 控制报文是 RREQ、RREP、RERR在 trace 文件里它们会以cbr之外的类型出现。更简单的做法是在 AODV 的收发函数里维护一个计数器在send_request、send_reply、send_error里分别累加仿真结束后打印出来。这比解析 trace 更可靠因为 trace 里控制报文的标记在不同版本里并不统一。我一般会在 aodv.h 里加三个 public 成员变量仿真结束时从 Tcl 里取$ns_ at 200.0 puts \RREQ[expr [$node_(0) set rreq_count_]]\5. AODV 仿真避坑序列号翻转、路由超时与广播风暴5.1 现象RREQ 洪泛导致仿真卡死仿真配置好后跑着跑着突然卡住不动CPU 占用 100%过一会 OOM 被杀。看 trace 文件发现全是 RREQ数据包几乎没发出去。原因目的节点不可达时AODV 协议没有实现 RREQ 重试上限或者最大重试次数设置得过大。RREQ 每次超时后重传等待时间翻倍但理论上报文的 TTL 也在翻倍导致广播范围越来越大最终整个网络都被 RREQ 淹没。另一个诱因是 NET_TRAVERSAL_TIME 设得太小RREP 还没回来源节点就判定超时重发。解决检查send_request()里是否有rreq_count限制标准值是 2超过后宣告目的不可达同时把 NET_TRAVERSAL_TIME 调整到至少 0.05 秒。如果是 NS2 默认实现直接改aodv.cc里的这三个常量重新编译。5.2 现象路由表项一直有效数据却丢包trace 显示源节点路由表里有到目的地的下一跳转发也正常但目的节点收不到中间节点没有报 RERR。原因路由表项的超时时间设置得比链路实际断开时间长得多。比如 ACTIVE_ROUTE_TIMEOUT 是 3 秒但无线链路因为移动已经断开中间节点没有感知到链路断还在按旧路由转发。等到真正发下一跳时才发现失败但那时源节点已经丢了不少包。解决缩短 ACTIVE_ROUTE_TIMEOUT或者开启 MAC 层的邻居丢失检测。在 NS2 的 802.11 实现里可以通过mac-trace和ifq丢包日志定位具体断链时间。我更推荐的做法是抓出丢包节点的 tcl 脚本位置回放那几秒的移动轨迹看看是不是两节点刚好擦肩而过导致瞬时断链。5.3 现象RERR 没触发路由黑洞某个节点发现自己下一跳不可达但它既不发 RERR也不发起本地修复数据包送到它就石沉大海。原因协议栈里recv_error没有被正确注册到路由表更新逻辑上。常见错误是 aodv.cc 里收到 RERR 后只是打印日志没有调用路由表的invalid_dst处理。另外一个原因是该节点认为自己在本地修复期间但修复请求超时后没有回退到 RERR导致状态卡死在修复中。解决检查 aodv.cc 的recv_error分支确认它遍历了路由表并以目的地址为键删掉了所有受影响条目。推荐打开 NS2 的 debug 宏明确看到 RERR 是否被生成和转发。本地修复超时后必须强制把路由状态从RTF_IN_REPAIR改成RTF_DOWN否则黑洞无法自愈。5.4 现象仿真结果每次都不一样同一份代码同一个脚本连着跑三次投递率和延迟差异非常大。原因NS2 的随机数种子没有固定。无线信道、节点移动、数据包间隔都依赖随机数。默认情况下每次启动取的种子不同结果自然不同。还有一种情况是脚本里在set dest前没有统一设置rng-random种子。解决在 tcl 脚本开头加上随机种子设置$ns_ use-newtrace set rng [new RNG] $rng seed 12345再用-seed 12345启动 ns。跑多次取平均时每次换一个种子但保证种子列表固定比如 12345、12346、12347。这样实验可复现结果可比。5.5 现象ns 版本和编译器导致编译失败make 时报一堆error: ‘Scheduler’ has not been declared或者stoi is not a member of std。原因NS2.35 是 2011 年的代码默认用老 GCC 编译。新系统里 GCC 10 以上移除了很多老接口也把隐式转换限制得更严格。最常见的坑是-Werror把警告当错误导致头文件里的旧式转型全挂。解决不要在裸机上死磕。最省事的方案是拉一个 Ubuntu 16.04 的 Docker 镜像在里面装gcc-4.8然后用./configure make编译。如果必须本机编译至少加-Wno-unused-variable -Wno-write-strings关掉几个顽固警告并在 Makefile 里把-Werror删掉。这里多说一句NS3 里的 AODV 是独立模块不需要这三个头文件直接把src/aodv/model/下的代码拷出来改即可别把 NS2 的头文件硬搬到 NS3两者架构完全不同。6. 让仿真结果可信从 trace 文件手工验证到参数敏感性分析仿真跑完不能只看最后打出来的几个数字你要先证明路由协议确实按照 AODV 的逻辑走了。我最常做的验证是用 grep 从 trace 里抽出 RREQ 和 RREP 事件核对源节点和目的节点序列号grep AODV out.tr | grep RREQ | head -20 grep AODV out.tr | grep RREP | head -20如果你手里的 AODV 实现没有在 trace 里打标签那就需要改源码在send_request和send_reply里加 printf。这个工作看起来很蠢但真的能救你一次。我记得有一次仿真结果特别好投递率 99%后来加日志发现源节点根本就没发过 RREQ所有包都是因为节点静止在同一位置靠 MAC 层广播传到的根本不是 AODV 在工作。从那以后我每次仿真都会先抓一条 RREQ→RREP 的完整记录做人工确认再谈后面的性能指标。验证完协议行为再做参数敏感性分析。AODV 最值得扫的参数有三个节点移动速度、节点密度、CBR 发包速率。以速度为例子把随机路点的速度区间从 5 到 30 m/s 分成四档5 m/s、10 m/s、20 m/s、30 m/s。每档跑 5 次取平均。你会看到投递率一般随速度升高先缓降到 20 m/s 后跳水。这时不要急着怪协议先回头查 RERR 的数量如果 RERR 也同步增长说明路由失效检测是正常的如果 RERR 没有明显上升那说明断链事件根本没被感知问题出在链路层反馈上。另一个值得扫的是NET_TRAVERSAL_TIME。这个参数从 0.03 改到 0.1端到端延迟可能翻倍但投递率会改善。如果你在写论文可以把这一条作为 AODV 在车载场景下的调参结论默认值偏向静态网络高移动性下需要放大建路超时换取更可靠的路由建立成功率。同时还要注意路由开销的同步曲线RREQ 洪泛增加的幅度如果不控制在 30% 以内说明你只是把问题延后了没有根治。最后给你一个我自己的判断习惯任何 AODV 仿真结果先做 RELIABILITY CHECK。取一次仿真的完整 trace挑一条端到端传输路径手工数出中间经过了几跳对比协议里记录的 hop_count。如果对数一致再往下看延迟分布对数不一致说明 trace 解析逻辑或者路由统计代码有 bug先修代码再谈性能。做仿真的人最怕拿到一个跑得出数字、但不知道数字怎么来的黑匣子。AODV 的源码本来就不长三个头文件把骨架都摆出来了花两个小时把报文构造、路由表插入、超时处理全读一遍比盲目调参十次都管用。希望这些踩过的坑能帮你绕开我当年的弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站