简介移动自组网Ad Hoc Network仿真项目中的AODV路由协议实现源码包以三个核心头文件构成面向网络研究者与仿真初学者重点解决无固定基础设施的动态多跳环境中路由模块快速搭建问题。压缩包共3个文件均为.h头文件整体仅8KB分别定义协议基本结构、报文封装格式与路由表项管理构成AODV模块的完整骨架结构精炼便于直接阅读和二次开发。已有243人学习下载适用于车载通信、应急通信及军事通信等场景的仿真验证。AODV属于按需路由协议仅在通信需要时发起路由发现可有效降低控制开销代码围绕RREQ、RREP、RERR三类报文的交互机制展开完整呈现路由建立、维护与失效处理流程。结合路由表的存储与转发决策逻辑读者可以深入理解协议工作细节并在此基础上扩展仿真实验分析端到端延迟、吞吐量与路由稳定性等关键指标为评估协议在移动自组网环境中的适应性提供基础也可作为教学示例或移植蓝本。1. AODV、移动自组网与仿真这包 rar 到底在做什么打开 aodv.rar里面十有八九是一套围绕 AODV 路由协议的移动自组网仿真工程TCL 场景脚本、trace 日志和几段统计脚本。移动自组网MANET指没有基站、没有固定基础设施节点一边移动一边通过无线链路互相转发的网络AODV 则是其中最常见的按需路由协议。仿真要回答的核心问题很实际节点在固定区域里随机跑动时AODV 能不能把数据包送到目标节点端到端时延多大控制开销高不高。下面按做实验的路子把环境搭建、场景脚本、结果统计和最容易翻车的地方过一遍。打算拿 AODV 做对比实验、写课程设计或者毕业论文的人可以直接沿着这条线往下走不用从头看协议论文。2. AODV协议原理与仿真器选型四个核心机制和一把配置参数2.1 AODV在移动自组网里怎么工作路由发现、返回、维护与本地修复AODV 全称 Ad-hoc On-demand Distance Vector Routing按需距离矢量路由。它和 DSDV 这类表驱动协议最大的区别是不主动维护全网路由表只有源节点要发包且手上没有可用路由时才发起一次路由发现。一次完整流程是这样源节点广播 RREQ路由请求中间节点收到 RREQ 后先看自己有没有到目标节点的有效路由。如果有且目标序列号足够新就回 RREP如果没有继续转发 RREQ 并记录反向路径。RREP 沿着反向路径单播回源节点沿途节点在路由表里写入到目标节点的正向路由。这个机制保证了数据包实际要发的时候路由才被建立没有路由的节点不会因为维护用不上的表项浪费无线带宽。这里有个关键点就是目标序列号。AODV 用序列号判断一条路由“新不新鲜”序列号越大代表路由越新。收到 RREP 的节点会比较当前路由的序列号和 RREP 里带的目标序列号只有新的才会更新路由表。序列号同时解决了环路问题因为每条路由都绑定了目标序列号老路由不可能覆盖新路由环路在实际走包过程中很难形成。做仿真时如果发现路径时通时不通先看序列号相关的参数别急着怀疑 MAC 层。断链处理也值得说。节点发现下一跳不可达时会生成 RERR路由错误发给所有使用这条链路的源节点。源节点收到 RERR 后删除对应路由表项如果还有数据要发就重新发起 RREQ。AODV 还支持本地修复断链节点如果离目标跳数不远先尝试在局部广播 RREQ 重建链路修复不了再向源节点报 RERR。这个机制对端到端时延影响很大仿真里观察时延抖动的时候很多尖峰就是本地修复失败后源端重新路由发现造成的。理解协议之后仿真里真正能调的参数来自 RFC 3561。下面这张表是 AODV 最核心的一组常量也是仿真实验里最常改的旋钮。参数名RFC 建议值作用HELLO_INTERVAL1000 ms节点周期性发送 Hello 消息维护邻居连通性ALLOWED_HELLO_LOSS2连续丢失几个 Hello 判定链路断开ACTIVE_ROUTE_TIMEOUT3000 ms路由条目被认为有效的时间NODE_TRAVERSAL_TIME40 ms单跳传输时延估算影响 RREQ 等待时间RREQ_RETRIES2路由发现失败后的最大重试次数RREQ_RATELIMIT10每秒最多可以发送的 RREQ 数量仿真器里这几个参数的实际默认值跟 RFC 建议值经常不完全一致因为 NS2 的 AODV 实现是从更早版本沿用过来的。改参数之前先翻开 aodv.cc 确认默认值再动手。否则论文里写“采用 RFC 3561 建议参数”实际仿真跑的是另一套值评审一追问就露馅。2.2 NS2、NS3还是OMNeTAODV仿真器的选型对比与选型理由移动自组网仿真目前最常用的三套工具是 NS2、NS3 和 OMNeT。虽然很多人把网络仿真想成和电机仿真、电路仿真类似的操作但网络仿真的抽象层级完全不一样你要关注的是包的走向、队列延迟和路由表变化而不是电压电流曲线或者信号波形。选错工具意味着后续所有统计脚本都要推倒重来所以这一步值得花时间。很多 AODV 相关的压缩包资料都基于 NS2原因很现实NS2 对 AODV 的支持最老牌RFC 3561 刚出来那几年研究者就是拿 NS2 做验证的。网上能找到的 AODV 场景脚本、trace 解析脚本、避坑笔记绝大多数也是围绕 NS2 写的。NS3 的 AODV 实现更现代、代码更干净但 trace 格式和 NS2 完全不同从 NS2 迁移到 NS3 几乎等于重学统计脚本写法。OMNeT 的 INET 框架也实现了 AODV适合做更复杂的协议栈实验但学习成本明显更高。下面按实际做实验的维度做对比。维度NS2NS3OMNeTAODV 实现成熟度最完整案例最多功能完整但版本迭代快INET 中有实现封装较重学习成本低到中教程量大中高需要理解模块化架构高需要熟悉 NED 和消息机制trace 可读性文本行格式awk 直接处理输出字段更全但字段分散事件记录按模块分离分析难度大典型用途协议验证、课程设计、论文对比大规模仿真、新协议开发复杂异构网络仿真我的建议是先看手上 aodv.rar 里是哪种工程。如果里面有.tcl文件那是 NS2 或 NS 系列直接按 NS2 搭环境。如果里面是 C 项目结构才考虑 NS3。不要因为 NS3 听起来更新就强行迁移除非你只是想学 NS3 本身否则单是重写 trace 解析脚本就够消耗大量时间。3. 用NS2跑通AODV仿真的最小实验从tcl脚本到trace文件3.1 环境准备NS2 安装、验证与文件清单确认NS2 已经停止更新但现有版本在主流 Linux 发行版上的兼容性要靠一些技巧。如果你用的是 Ubuntu 18.04 以上系统直接编译 ns-allinone 大概率会遇到 GCC 兼容问题。常见做法是装一个 Ubuntu 16.04 虚拟机或者直接使用别人打好的 NS2 docker 镜像省掉编译血的教训。我自己一般用容器方案镜像里面环境已经固定跑出来的 trace 不受宿主机工具链影响后续复现也容易。装好 NS2 后先验证一下环境能正常跑无线场景。在命令行输入ns进入交互模式再退出确认可执行文件在 PATH 里。然后检查setdest工具是否可用这个工具在 NS2 安装目录下的indep-utils/cmu-scen-gen/setdest目录里。很多实测翻车都发生在这一步主程序能启动但 setdest 生成移动轨迹时报错导致后面节点坐标和移动路径都是空的。环境准备好之后拆开 aodv.rar 看看里面的组成。最常见的结构是一个或几个.tcl主脚本定义仿真场景、节点数量、业务流一组由 setdest 生成的移动轨迹文件通常是move.tcl或类似命名一段或多段用于统计指标的 awk 脚本README 或者实验报告说明记录运行方式和参数先看 README明确这个实验的节点数、区域大小、业务类型和仿真时长。这些参数直接影响你对结果的判断跑之前不确认清楚后面统计出来的指标没法对齐到具体场景。3.2 最小可运行的AODV无线场景脚本TCL配置与关键参数说明现在写一个 50 节点、1000×1000 米区域、仿真 300 秒的最小可运行 AODV 场景。下面这个脚本是完整框架可以直接保存成aodv_min.tcl运行。# 创建仿真器对象打开 trace 与 nam 输出文件 set ns [new Simulator] set tracefd [open aodv.tr w] $ns trace-all $tracefd set namfd [open aodv.nam w] $ns namtrace-all-wireless $namfd 1000 1000 # 定义无线地形对象和 god 对象 set topo [new Topography] $topo load_flatgrid 1000 1000 set god [create-god 50] # 节点级参数配置AODV 路由 802.11 MAC $ns node-config \ -adhocRouting AODV \ -llType LL \ -macType Mac/802_11 \ -ifqType Queue/DropTail/PriQueue \ -ifqLen 50 \ -antType Antenna/OmniAntenna \ -propType Propagation/TwoRayGround \ -phyType Phy/WirelessPhy \ -channelType Channel/WirelessChannel \ -topoInstance $topo \ -agentTrace ON \ -routerTrace ON \ -macTrace OFF # 载入setdest生成的节点初始位置与移动轨迹 source move.tcl # 建立一组CBR业务流节点0发送到节点10 set udp0 [new Agent/UDP] $ns attach-agent $n0 $udp0 set null0 [new Agent/Null] $ns attach-agent $n10 $null0 $ns connect $udp0 $null0 set cbr0 [new Application/Traffic/CBR] $cbr0 set packetSize_ 512 $cbr0 set interval_ 0.01 $cbr0 attach-agent $udp0 $ns at 10.0 $cbr0 start $ns at 290.0 $cbr0 stop # 仿真结束时冲刷trace缓冲并退出 proc stop {} { global ns tracefd namfd $ns flush-trace close $tracefd close $namfd exit 0 } $ns at 300.0 stop $ns run这段脚本最关键的是$ns node-config部分。-adhocRouting AODV指定路由协议-ifqLen 50是接口队列长度会影响丢包行为队列太短会导致突发流量大量丢弃-propType Propagation/TwoRayGround用双径地面传播模型而不是简单的自由空间模型这个选择意味着距离超过一定阈值的节点收不到信号更接近真实地面环境-agentTrace ON和-routerTrace ON负责输出应用层和路由层的 trace这两行必须打开否则第 4 章的统计脚本拿不到数据。CBR 业务的interval_ 0.01表示每 10 毫秒发一个包配合packetSize_ 512字节实际吞吐约 400 kbps。你要跑更高负载就把 interval 改小但注意别让网络饱和否则 AODV 的路由开销和业务流量互相挤压结果全部变成拥塞指标协议本身的特性反而看不出来。3.3 生成随机移动轨迹setdest 工具参数与固定 seed 技巧AODV 仿真的目的是观察节点移动下的路由行为所以移动模型直接影响结论。NS2 自带 setdest 工具生成 random waypoint 移动轨迹。命令如下。./setdest -v 2 -n 50 -p 5 -M 10 -t 300 -x 1000 -y 1000 move.tcl-v 2表示输出格式为 NS2 可识别的 TCL 语句-n 50是节点数必须和主脚本里create-god 50一致-p 5是节点到达目标点后的暂停时间单位秒-M 10是节点最大移动速度单位米/秒-t 300是生成移动轨迹的总时长要和仿真时长匹配-x和-y是区域尺寸。实际做参数扫描时移动速度是最常调整的变量。低速时 AODV 路由稳定投递率高速度超过 20 m/s 后链路频繁断开即使 AODV 能快速重建路由端到端时延也会明显上升。如果你要做“不同速度下 AODV 性能对比”这类实验每次只改-M值其余参数保持完全一致。setdest 本身有随机种子选项不同系统下默认 seed 不同导致每次生成轨迹不一样。做实验前先固定 seed给 setdest 加-s参数并传入一个固定数字保证多轮实验用的是同一批移动轨迹只有其他变量在变否则结果没法对比。3.4 跑通后的三件事trace行数、路由事件和nam动画验证运行ns aodv_min.tcl后正常会产生aodv.tr和aodv.nam两个文件。先别急着统计做三个快速检查。第一看 trace 文件行数。执行wc -l aodv.tr如果只有几百行说明业务流或移动轨迹没有正常加载。一个跑满 300 秒、50 节点的 AODV 场景trace 行数至少几万行。第二搜索路由事件。执行grep aodv aodv.tr | head应该能看到 RREQ、RREP、RERR 相关的 trace 行。如果一行都搜不到说明路由建立过程没发生大概率是源节点和目标节点之间一直有可用路由或业务流根本没启动。第三用 nam 播放动画确认节点初始位置分散在整个区域而不是挤在一起。nam 动画里如果看到大量红色丢包标记直接去 trace 里按时间点查丢包原因。这三项都过了说明场景脚本本身没问题可以进入指标统计阶段。4. 从trace里挖出协议指标时延、投递率与路由开销的统计脚本4.1 trace 行的字段对应事件、时间、节点、协议和包uid怎么看NS2 无线 trace 每行代表一个事件字段之间用空格分隔。以 NS2.35 默认输出为例一行典型记录长这样s 10.000000 _0_ AGT --- 0 cbr 512 [0 0 0 0] ------- [0:0 10:0 32 0] [0] 1 0对照字段字段位置含义统计时的用途行首字母s 发送r 接收d 丢弃f 转发决定统计哪类事件第 2 字段事件发生时刻单位秒算时延和时间窗口第 3 字段产生事件的节点编号按节点维度做分析第 4 字段事件所在层AGT、RTR、LL、MACAGT 是应用层RTR 是路由层包类型字段如 cbr、aodv、arp区分业务包和控制包最后一个字段包唯一编号收发配对的关键统计指标时最常犯的错误是只按包类型过滤不按层过滤。比如统计投递率时如果不过滤 AGT 层会把网络层转发的包也算进接收数投递率直接超过 100%。反过来统计 AODV 控制开销时必须限定 RTR 层因为路由协议包不经过应用层。4.2 投递率和端到端时延一个awk脚本算出两个核心指标下面的 awk 脚本统计 CBR 业务包的投递率和平均端到端时延。核心思路是发送事件产生时记录包 uid 对应的时间戳接收事件到达时用接收时间减去发送时间得到单包端到端时延。#!/usr/bin/awk -f # 统计 AODV 仿真 trace 中 CBR 包的投递率与平均端到端时延 # 用法: awk -f stat_delivery.awk aodv.tr /^s/ /AGT/ /cbr/ { send_num uid $NF # 取最后一个字段作为包唯一编号 send_time[uid] $2 # 记录发送时刻 } /^r/ /AGT/ /cbr/ { recv_num uid $NF if (uid in send_time) { delay_sum ($2 - send_time[uid]) delay_cnt } } END { printf 发送 CBR 包数: %d\n, send_num printf 接收 CBR 包数: %d\n, recv_num if (send_num 0) printf 投递率: %.2f%%\n, 100.0 * recv_num / send_num if (delay_cnt 0) printf 平均端到端时延: %.6f s\n, delay_sum / delay_cnt }这个脚本有几处需要解释。匹配条件同时包含^s、AGT、cbr三重条件缺一不可。只看s和r会混入 MAC 层重传、路由包和 ARP 包只看包类型会混入转发事件。uid $NF取的是 trace 行最后一个字段NS2 的 trace 格式里这个字段是包唯一编号发送事件和接收事件的 uid 一一对应才能把时延算准。如果 trace 里看不到 AGT 层回去检查第 3.2 节的脚本是不是把-agentTrace ON打开了。这个选项控制应用层 trace关掉后所有 AGT 事件都不会输出投递率和时延自然没法算。跑实验时最早的教训就是漏了这行统计脚本怎样都输出零。4.3 路由开销统计AODV控制包数量的过滤与归一化路由开销是自组网协议对比里绕不开的指标它的定义是路由控制报文占网络总报文的比例。在 trace 里统计时把包类型等于 aodv 的事件单独数出来。#!/usr/bin/awk -f # 统计 AODV 路由控制包的发送数量 # 用法: awk -f stat_aodv_ctrl.awk aodv.tr /^s/ /RTR/ /AODV/ { ctrl_send len_sum $8 } END { printf 发送 AODV 控制包数: %d\n, ctrl_send }这里限定RTR层是因为 AODV 协议控制消息由路由代理直接产生不会进入 AGT 层。如果不过滤 RTR统计时会数出两套重复计数。$8是包大小字段累加起来后可以算控制开销的字节占比。路由开销不仅看数量还要换算成比例才公平。常见做法是统计所有传输事件的总字节数再单独统计 AODV 控制包的字节数两者相除得到一个比例值。单独控制包数量没有意义因为不同场景的节点数不同业务流负载也不同。画图时我习惯先把 trace 按时间窗口切分再统计每个窗口内的时延均值用 gnuplot 画时延随时间变化的曲线。脚本如下。set terminal png set output delay_time.png set xlabel 仿真时间 (s) set ylabel 平均端到端时延 (s) plot delay_window.dat using 1:2 with lines title AODV 时延delay_window.dat的第一列是窗口起始时间第二列是该窗口内时延均值。窗口大小通常选 5 秒。窗口太小时延曲线全是毛刺窗口太大又看不到本地修复引发的时延尖峰。5. AODV仿真避坑记录5个让结果翻车的常见问题5.1 现象NS2 编译不过或仿真中途崩溃在比较新的 Ubuntu 系统上直接编译 ns-allinone会遇到 GCC 对旧代码的兼容性报错编译到一半失败即使编译成功跑仿真时偶尔也会出现莫名的崩溃退出。这个问题在刚接触 AODV 仿真的环境搭建阶段最容易让人原地劝退。原因是 NS2 的 C 代码停留在 C98 时代新编译器对隐式转换和内存模型的处理更严格老代码里的很多写法在新标准下直接报错。解决的办法不是去改源码而是用一个验证过的环境。我一般使用 NS2 drocker 镜像里面已经把补丁打好跑同样的 TCL 脚本结果稳定如果必须在本机装就把系统固定在 Ubuntu 16.04 或 CentOS 6 这类旧版本上省去不必要的折腾。5.2 现象NAM 动画里节点不动或全部挤在角落跑完仿真后打开 NAM发现所有节点堆在区域的一个角落或者干脆静止不动。看起来像是仿真没跑起来但 trace 文件又正常生成。这个问题通常出在source move.tcl这里的坑setdest 生成的脚本里变量名是$node_()形式而主脚本里如果用$node()创建节点两者根本不是同一套变量坐标语句就会被静默忽略。解决方法是打开 move.tcl 看前几行确认变量名风格和主脚本统一起来。另一个相关原因是setdest的版本和 NS2 版本不匹配比如用了 NS3 配套的生成工具输出的格式完全不能被 NS2 解析。5.3 现象换成 TCP 业务后投递率突然暴跌同一个场景用 UDPCBR 跑投递率能到 90% 以上改成 TCPFTP 后掉到 40%大量重传时延也变大。这不是 AODV 协议本身“坏掉了”而是 TCP 的重传超时和 AODV 的路由重建时间互相打架。TCP 发送端超时重传的粒度一般是几百毫秒到秒级而 AODV 通过 Hello 机制发现链路断开需要好几个 Hello 周期链路重建又要一次 RREQ 往返。这两个时间尺度的错位会导致 TCP 已经判定超时而 AODV 还在找新路由数据面上自然一片重传。要单独评估 AODV 的路由性能先用 CBR 业务做想评估真实业务表现就增大 ALLOWED_HELLO_LOSS 和 ACTIVE_ROUTE_TIMEOUT给路由重建留出余量。5.4 现象只改随机种子结果像开奖同一个 TCL 脚本每次运行结果都不一样投递率在 60% 到 95% 之间大幅波动。很多人这时候怀疑是协议实现有 BUG其实问题出在移动轨迹和业务流的随机性没有被固定。setdest 生成移动轨迹时用的随机种子、CBR 业务流的启动抖动都会影响最终结果。正确做法是先固定 setdest 的种子让所有实验共用同一批移动轨迹再固定 TCL 脚本里的随机种子。报告结果时用同一组参数跑 5 个不同种子取均值加标准差而不是只报一次运行的结果。只跑一次就把数据写进论文基本属于给自己埋雷。5.5 现象统计投递率超过 100% 或时延大得离谱awk 统计出来的投递率超过 100%或者平均时延到几十秒一看就是统计口径出问题。大多数情况是过滤条件少了层级限制把 MAC 层重传、路由层转发都算成了接收。比如只写$1r和cbr会把中间节点的转发事件也算成一次“接收”自然高于实际到达应用层的数量。修法就是第 4 章的过滤条件^r、AGT、cbr三个条件缺一不可再拿wc -l对比一下 trace 行数和统计到的包数是否在合理范围内能拦截大部分统计错误。6. 让AODV仿真结果更可信参数扫描与多seed交叉验证6.1 用 bash 循环做参数扫描直接生成结果表做 AODV 对比实验时通常要扫节点数、移动速度、业务负载这几个维度。手工一个个跑不现实必须用脚本批量执行。下面的 bash 循环按不同移动速度和不同 seed 组织实验每个实验单独保存 trace 文件。#!/bin/bash # 批量跑 AODV 仿真速度从 5 到 20 m/s每档跑 3 个 seed for speed in 5 10 15 20 do ./setdest -v 2 -n 50 -p 5 -M $speed -t 300 -x 1000 -y 1000 move_${speed}.tcl for seed in 1 2 3 do sed s/set seed 1/set seed $seed/ aodv_min.tcl run_tmp.tcl sed s/move.tcl/move_${speed}.tcl/ run_tmp.tcl run_${speed}_${seed}.tcl ns run_${speed}_${seed}.tcl mv aodv.tr result_${speed}_${seed}.tr awk -f stat_delivery.awk result_${speed}_${seed}.tr summary_${speed}.txt done done这个脚本的每一行都很机械只有一个细节值得注意sed替换时必须把 seed 和移动轨迹文件都换成当前档位的值否则所有实验都在跑同一批轨迹参数扫描就失去了意义。跑完以后summary_*.txt里已经有每个 seed 的投递率和时延用表格软件汇总求均值。6.2 交叉验证同一场景下对比 DSDV或者换 NS3 复跑AODV 的仿真结果要让人信服光看它自己跑出一条曲线是不够的。常见做法是拿同一个移动轨迹和业务流把路由协议换成 DSDV跑出一组对比数据。DSDV 是表驱动协议路由开销和时延特征跟 AODV 差异大到可视化图表一眼能看出来这也是论文里最常见的对照组。有条件的话把同一组参数放到 NS3 里再跑一遍对比投递率是否在同一量级。NS2 和 NS3 的 AODV 实现细节不完全一致数值上允许有偏差但方向应该相同低速时高投递率、高速时投递率下降、控制开销随节点数增加。如果两个仿真器得出的结论方向相反先回去检查场景参数是不是真的对应上了。6.3 多 seed 平均值的坑均值好看不代表单次稳定参数扫描跑完最容易栽在“只看均值不看方差”上。AODV 是移动场景协议单次 seed 的结果噪声很大均值能到 90%但单个 seed 可能只有 70%。这种波动在低速场景尤其明显少量几个节点移动它们的轨迹碰上或错开直接决定整条业务流的命运。我自己最早做这组实验时就吃过单 seed 的亏。一档参数只跑了一次得出来的结论和后来 5 个 seed 平均后的结论完全相反等于白做一轮。后来改成每组参数至少 3 到 5 个 seed数值稳定才往下走。做参数扫描宁可少扫几档速度也要保证每个点都有重复实验支撑。希望这个习惯能帮你少走一段弯路做 AODV 仿真时让结论站得住。本文还有配套的精品资源点击获取