简介:这是一份网络仿真软件包 ns-allinone 2.26 完整发行版,整合了经典网络模拟器 ns-2 及配套组件,适合网络专业学生、研究人员及工程师用于学习与实验。该版本重点支持 MAC 层与路由层仿真,可构建无线/有线网络场景,测试路由策略、QoS、传输控制等协议行为。压缩包内含 5056 个文件,总大小约 50.44MB,以 tcl 脚本、c/cc/h 源码、测试用例 (test)、nam 动画文件、文档 (tex/html/readme) 等为主,覆盖从编译配置到仿真实例的完整体系。已有 108 人学习参考。解压后用户可依据标准流程配置编译,运行内置示例场景并观察网络流量、延迟、丢包等输出,也可修改源码扩展算法,是深入理解网络协议机制与开展仿真研究的实用工具。
1. NS-allinone-2.26 到底装了什么:为什么二十年老仿真器还有人在用
拿到ns-allinone-2.26.tar.gz这个文件时,多半意味着你正在复现一篇十几年前论文里的无线网络实验,或者被导师要求“用 NS-2 把某某协议跑一下”。这个包解决的是 NS-2.26 本体和它依赖的 Tcl/Tk、OTcl、tclcl、nam、xgraph 一起打包的问题,allinone 这个名字名副其实:一个 tar.gz 里把能跑通整套仿真环境的所有组件都绑定了。
这个版本确实老,但老不等于没人用。NS-2.26 至今仍是很多课程、论文复现和技术博客里被点名指定的版本,因为大量现成的协议实现和 Tcl 脚本都是按老 API 写的,用 NS-3 或者新版 NS-2 反而跑不起来。对还在做 Ad Hoc 网络、无线传感器网络研究的人来说,这个包就是他们离不开的工作台。
但它有个很现实的问题:一套 2005 年左右的 C++ 代码,放在今天的 Linux 发行版上,几乎不可能一次编译通过。这篇笔记就说清楚三件事:怎么把这套 tar.gz 在能接受的系统上装起来、怎么跑通第一个仿真、以及真正让你浪费时间的高频坑在哪。
2. 用 tar.gz 源码包编译 ns-2.26:configure/make 三步走与参数选型
2.1 为什么 allinone 比逐组件安装靠谱
很多人一上来想自己分开装:先装 Tcl/Tk,再装 OTcl,再装 tclcl,最后编 NS-2 和 nam。这个思路本身没错,但版本配套非常折磨人。NS-2.26 当年绑定的是 Tcl/Tk 8.4.x,而现代 Linux 发行版自带的往往是 8.6 甚至更新版本。Tcl 8.6 对解释器初始化和事件循环的接口做了调整,NS-2.26 的 otcl 和 tclcl 代码编译能过,但运行时经常莫名其妙崩溃,或者 nam 启动后立刻闪退,报错信息还不直观。
allinone 的价值就在这里:它把 NS-2.26 当时验证过的组件版本锁死在一个压缩包里。tcl、tk、otcl、tclcl、ns、nam、xgraph 的版本互相匹配,编译顺序也是安装脚本编排好的。你只要保证系统里有合适的编译器和 X11 开发库,剩下的事情基本自动完成。
我一般建议:只要手头有这个 allinone 包,就不要去拆开装。自己拼版本遇到的那些玄学问题,大多是因为某个子组件版本和预期不一致。
2.2 编译前的环境检查:gcc、make、X11 三个维度
先别急着解压,花两分钟确认系统环境。以下命令在终端里执行一下:
gcc --version g++ --version make --version pkg-config --modversion x11 || echo "no x11 dev" dpkg -l | grep libxmu-dev第一组命令看编译器版本,第二组看 X11 开发库是否存在。NS-2.26 的年代,gcc 4.x 是主流,代码里用了一些旧的 C++ 写法。如果你机器上是 gcc 9 以上,后面编译大概率会在某个组件的 C++ 源码上报错,这是正常的,不是你的操作问题。
libxmu-dev这类包是 nam 和 xgraph 编译时要用的 X 窗口工具库。缺少它,configure 阶段就会失败,错误信息通常是找不到X11/Xlib.h或者-lXmu。
提示:如果看到
X11/Xlib.h: No such file or directory,不用去翻 NS 源码,直接装 X11 开发包即可。
另外,安装路径不要带中文、空格和奇怪字符。allinone 的安装脚本会生成一批带相对路径的链接,路径里一个空格就能让后续很多命令找不到文件。我一般放在/home/ns/ns-allinone-2.26这种干净目录。
2.3 ./install 一条命令完成三层组件的编译与安装
把包解压后,在ns-allinone-2.26/目录下执行:
cd /home/ns/ns-allinone-2.26 ./install./install做的事情,本质上就是先 configure 再 make 再 make install,但顺序是写死的:先 tcl、tk,然后 otcl、tclcl,再 ns,最后 nam 和 xgraph。你不用手动介入每个子目录。
如果因为某些原因不能跑./install,退一步手动操作也行:
./configure --with-tcl=/home/ns/ns-allinone-2.26/tcl8.4.x \ --with-tk=/home/ns/ns-allinone-2.26/tk8.4.x make make install注意这里我用8.4.x占位,实际目录名以你解压出来的为准,ls 看一眼就知道。--with-tcl和--with-tk参数是告诉 configure,去哪个目录找 Tcl/Tk 的开发文件,allinone 里已经带了这两个包,所以路径指向 allinone 内部即可,不要指向系统自带的/usr/lib。
编译过程会持续十几分钟到半小时。如果中途失败,不要重新跑整个 install,先看失败的是哪个子目录,进入那个目录执行make,输出会直接告诉你在哪个文件哪一行出了问题。
2.4 环境变量和第一个自检指令
编译成功后,真正容易翻车的地方才来:环境变量。NS-2.26 运行时要加载自己编译出来的动态库,这些库不在系统默认路径里,你不告诉系统它们在哪儿,ns命令就永远提示找不到共享库。
我习惯把以下内容写进~/.bashrc:
export NS_HOME=/home/ns/ns-allinone-2.26 export PATH=$NS_HOME/bin:$NS_HOME/tcl8.4.x/unix:$NS_HOME/tk8.4.x/unix:$PATH export LD_LIBRARY_PATH=$NS_HOME/otcl-1.x:$NS_HOME/lib:$LD_LIBRARY_PATH export TCL_LIBRARY=$NS_HOME/tcl8.4.x/library然后source ~/.bashrc,执行两个自检命令:
ns -v ldd $(which ns) | grep tclns -v正常时会输出 ns-2.26 的版本信息,说明主程序能跑起来。ldd检查动态库链接情况,如果输出里出现libtcl8.4.so => not found,说明 LD_LIBRARY_PATH 没写对,检查 allinone 目录里 otcl 和 lib 文件夹的真实名称。
注意:PATH 和 LD_LIBRARY_PATH 写错一个字符,症状都是“命令找不到”或“共享库找不到”,排查时先 echo 看一遍这两个变量。
2.5 编译报错时的参数兜底:CXXFLAGS 与编译器降级
很多人在 Ubuntu 22.04 以上版本编译 NS-2.26 时会遇到类似‘newer’ is not a member of ‘std’或undefined reference to ‘TclpThreadCreate’的报错。这些本质是编译器版本太新,老代码用了已经不存在的标准库接口。
常见的解决办法有两个。第一是降级编译器,安装 g++-4.8 并用CXX=g++-4.8加进 configure 环境;第二是调低 C++ 标准并加兼容宏:
make clean CXXFLAGS="-std=c++03 -DUSE_MEMCPY=1" ./install-DUSE_MEMCPY=1是 NS 社区流传很广的一个开关,用来绕开老代码和现代编译器在字符串拷贝上的冲突。如果你的系统没到 gcc 11 那么激进,这个参数通常够用;如果问题依旧,就去装老编译器,别跟源码较劲。
3. 跑通第一个 ns-2.26 仿真:写 Tcl 脚本、调 nam、读 trace 文件
3.1 从零写一个双节点无线 Tcl 脚本
NS-2 的仿真脚本是 Tcl 写的,先说清楚一件事:不要自己从空白文件手敲全部配置。NS-2 的无线模块有太多前置设置,漏一个create-god都跑不动。下面的脚本是一个最小双节点 Ad Hoc 仿真,可以直接保存为wireless.tcl运行。
# 无线场景配置 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(x) 500 set val(y) 500 set val(stop) 50.0 set ns [new Simulator] set tf [open out.tr w] $ns trace-all $tf set nf [open out.nam w] $ns namtrace-all-wireless $nf $val(x) $val(y) set topo [new Topography] $topo load_flatgrid $val(x) $val(y) create-god 2 set chan_1_ [new $val(chan)] # 节点配置 $ns node-config -adhocRouting DSDV \ -llType $val(ll) \ -macType $val(mac) \ -ifqType $val(ifq) \ -ifqLen 50 \ -antType $val(ant) \ -propType $val(prop) \ -phyType $val(netif) \ -channel $chan_1_ \ -topoInstance $topo \ -agentTrace ON \ -routerTrace ON \ -macTrace OFF set node_(0) [$ns node] set node_(1) [$ns node] # 节点初始位置与移动 $node_(0) set X_ 100.0 $node_(0) set Y_ 100.0 $node_(0) set Z_ 0.0 $node_(1) set X_ 400.0 $node_(1) set Y_ 400.0 $node_(1) set Z_ 0.0 $ns at 1.0 "$node_(0) setdest 200.0 300.0 20.0" $ns at 2.0 "$node_(1) setdest 300.0 100.0 20.0" # 建立 UDP 流量 set udp0 [new Agent/UDP] set null0 [new Agent/Null] $ns attach-agent $node_(0) $udp0 $ns attach-agent $node_(1) $null0 $ns connect $udp0 $null0 set cbr0 [new Application/Traffic/CBR] $cbr0 set packetSize_ 512 $cbr0 set interval_ 0.2 $cbr0 attach-agent $udp0 $ns at 1.0 "$cbr0 start" $ns at 49.0 "$cbr0 stop" # 结束 $ns at $val(stop) "stop" $ns at $val(stop) "puts \"sim done\"" $ns at $val(stop) "$ns halt" $ns run这个脚本里值得关注几个参数。interval_ 0.2表示每 0.2 秒发一个包,packetSize_ 512 是 512 字节,两者共同决定流量速率。DSDV 是路由协议,换成 AODV 或 DSR 也只需要改这一处字符串。create-god 2的 2 必须等于节点数,少了运行时会提示找不到 god 对象。
运行方式很简单:
ns wireless.tcl结束后目录下会生成out.tr和out.nam两个文件。前者是文本格式的 trace 记录,后者是 nam 动画的输入文件。
3.2 用 awk 把 trace 读成指标:丢包率、吞吐量
out.tr是一个纯文本事件日志,每一行记录一个事件,格式大概是:事件类型、时间、源节点、目标节点、层级、标志位……对刚接触的人来说这堆数字像天书,但用 awk 可以很快提取出关键指标。
统计丢包率的命令:
awk '$1=="s" && $4=="AGT" {sent++} $1=="r" && $4=="AGT" {recv++} $1=="d" && $4=="AGT" {drop++} END {printf "sent=%d recv=%d drop=%d loss=%.2f%%\n", sent, recv, drop, (sent-recv)/sent*100}' out.trs是发送事件(send),r是接收事件(receive),d是丢弃事件(drop)。$4=="AGT"表示只看应用层,避免把 MAC 层重传、路由层控制包都算进来。这个过滤条件很重要,不改的话统计出来的丢包率会偏低,因为大量被 MAC 层重传成功的包不算丢。
3.3 用 nam 可视化:能回放不代表协议对
nam 是 NS-2 自带的动画播放工具,运行:
nam out.nam你会看到两个节点按设定的 setdest 移动,UDP 数据包在中间传递,丢包时会出现红色的丢弃标记。nam 适合快速看网络拓扑和移动模型设计是否合理,但注意一个坑:动画流畅不代表协议行为正确。
比如 DSDV 路由不稳定时,包会频繁改路,nam 里看起来只是路径切换,但 trace 文件里可能已经出现了很多次!!的转发失败记录。所以我习惯先跑一遍 awk 统计,再打开 nam 确认现象,顺序反过来容易被动画误导。
4. ns-allinone 安装与运行的 5 个高频坑:现象、原因、解决
4.1 编译死在 C++ 标准库接口上
现象:编译到 ns-2.26 本体时,终端刷出一屏error: ‘newer’ is not a member of ‘std’,或者链接阶段报undefined reference to ‘TclpThreadCreate’。
原因:系统默认 gcc/g++ 版本太新。NS-2.26 的代码基于 C++98 时代的 STL,现代编译器做了大量接口清理和废弃,老代码踩中一两个就停摆。
解决:先试CXXFLAGS="-std=c++03 -DUSE_MEMCPY=1" ./install;还不行就装老编译器,比如 Ubuntu 上用apt install g++-4.8,再用CXX=g++-4.8重新 configure。不要试图手工改源码,老代码互相调用关系复杂,改一处往往牵出三处新错。
4.2 运行 ns 提示找不到 libtcl8.4.so
现象:编译全部成功,但执行ns wireless.tcl时立即报错error while loading shared libraries: libtcl8.4.so.0: cannot open shared object file。
原因:tcl 的动态库路径没有加进 LD_LIBRARY_PATH。allinone 编译出来的 tcl 库放在自己目录里,不被系统默认搜索,而ns二进制在运行时才去找它。
解决:确认$NS_HOME/tcl8.4.x/unix或$NS_HOME/lib下存在libtcl8.4.so,然后把对应目录写进 LD_LIBRARY_PATH。用ldd $(which ns)检查,直到输出不再有not found。
4.3 新终端里 ns 又“消失”了
现象:上一秒还能跑ns -v,关掉终端重新打开,又提示command not found。
原因:环境变量只写在了当前终端的会话里,没有持久化到 shell 配置文件。
解决:把 2.4 节的环境变量追加进~/.bashrc,然后执行source ~/.bashrc。这里有个细节:不要只加 PATH,LD_LIBRARY_PATH 和 TCL_LIBRARY 也必须一起加,否则会出现命令找得到但库找不到的割裂状态。
4.4 麒麟 V10 这类系统缺 X11 开发包
现象:configure 阶段报错X11/Xlib.h: No such file or directory,或者 nam 编译时找不到-lXmu。这个场景在麒麟 V10 这类国产 Linux 系统上尤其常见,默认安装通常不带完整开发环境。
原因:系统只装了运行库,没装编译期需要的 dev 包。X11 相关头文件属于libX11-dev、libxmu-dev、libxt-dev这些包。
解决:能联网就直接装开发包。对离线内网环境,我一般先把需要的 tar.gz 包推到内网私有仓库,再通过软件源分发到各台机器。这个推包动作可以借助 KubeKey 这类离线部署工具完成,它能把本地 tar.gz 制作成私有仓库可识别的格式,这样同一个 allinone 包就能在多台机器上重复安装,不用每台都手工传文件。关键点是:开发包齐了,configure 基本能一次过。
4.5 nam 打开动画闪退或无响应
现象:nam out.nam启动后窗口一闪而过,或者弹出错误couldn't read file "library/..."。
原因:两个方向。第一是 nam 依赖的 Tk 库路径不对,导致界面初始化失败;第二是没有图形显示环境,比如你用 SSH 连服务器跑。
解决:确认 LD_LIBRARY_PATH 包含 tk 的 unix 目录;远程环境用Xvfb :99 -screen 0 1024x768x24 &起虚拟显示,再export DISPLAY=:99,nam 就能在无头环境下跑起来。注意 nam 的动画导出也可以走这个虚拟显示方案,批量导出时很实用。
5. 把 ns-2.26 装进 Docker:批量仿真与 awk 解析 trace 的技巧
5.1 用老镜像把 allinone 做成可重复运行的环境
新系统上装 NS-2.26 的痛,本质是编译器和库版本代差太大。我现在所有 NS-2.26 工作都放进 Docker,宿主环境保持干净,换机器也能复现。
准备一个 Dockerfile:
FROM ubuntu:14.04 RUN apt-get update && apt-get install -y gcc g++ make \ xorg-dev libxmu-dev libxt-dev COPY ns-allinone-2.26.tar.gz /opt/ WORKDIR /opt RUN tar xzf ns-allinone-2.26.tar.gz && \ cd ns-allinone-2.26 && ./install ENV PATH=/opt/ns-allinone-2.26/bin:$PATH ENV LD_LIBRARY_PATH=/opt/ns-allinone-2.26/otcl-1.x:/opt/ns-allinone-2.26/lib:$LD_LIBRARY_PATH CMD ["ns", "-v"]选 ubuntu:14.04 而不是最新版,是因为它自带的 gcc 4.8 和老代码兼容性最好。构建时只需docker build -t ns226 .,运行仿真时用:
docker run -it -v $PWD/sim:/sim ns226 bash把本机 sim 目录挂载进容器,Tcl 脚本放在这个目录里,容器内外共享,非常利于批量处理。
5.2 批量跑参数扫描:把速率、种子循环起来
做仿真最常干的事就是改参数看趋势。手工改 Tcl 脚本里的 interval_ 再一个个跑,又慢又容易漏改文件名。我一般用 sed 生成脚本副本:
for rate in 100 200 400 800; do sed "s/interval_ 0.2/interval_ 0.1/" wireless.tcl > run_${rate}.tcl ns run_${rate}.tcl mv out.tr res_${rate}.tr done这个循环只演示了最简单的替换逻辑,实际上可以把 seed、节点数、发包速率全部参数化。关键是每次运行完后立刻把 out.tr 改名归档,否则下一轮仿真会覆盖上一轮结果。
5.3 结果验证的土办法:换 seed 重跑,看数字漂移
NS-2.26 的移动模型和随机数都有种子控制,但很多人在脚本里没显式设置,导致每次运行结果看似相同,其实随机数序列在变。验证结果是否稳定,我会用同一套参数换 seed 跑三遍,然后比较丢包率的波动范围。
用一行 awk 把丢包率统计到 CSV:
for f in res_*.tr; do awk -v name="$f" '$1=="s" && $4=="AGT" {s++} $1=="r" && $4=="AGT" {r++} END {printf "%s %.2f\n", name, (s-r)/s*100}' "$f" done > result.csv三遍跑下来,如果丢包率的波动在 5% 以内,说明趋势可信;波动大就加大仿真时长或换更大场景,别急着下结论。我现在装任何 NS-2.26 源码包都默认先进 Docker,编译参数写成注释放在 Dockerfile 旁边,避免两个月后自己也忘了当初是怎么跑通的。希望这些经验对你拿到 tar.gz 包后能少走一段弯路,特别是那些看起来像玄学的编译问题,其实大多能找到明确出处。
本文还有配套的精品资源,点击获取