简介:这是一份面向高校网络工程、软件定义网络课程实验的 Mininet 实验报告,适合正在学习网络仿真与 SDN 拓扑构建的本科生或自学者使用。报告以西安财经大学《网络应用设计与系统集成》实验一为蓝本,系统梳理了 Mininet 的核心概念、主要特性与常用命令,并通过三个层次展开实践:先使用可视化工具 Miniedit 创建拓扑,再通过命令行和交互式界面添加主机、交换机并执行 ping 测试,最后编写 Python 脚本构建 linear、single、tree 等交换拓扑,同时给出了主机 IP 配置和网络性能限制的示例。资源为 1 个 doc 文档,压缩包 1.37MB,共 16 页,完整记录实验目的、知识背景、操作步骤、关键配置与结果验证,可帮助读者对照完成仿真实验并理解 Mininet 的核心机制。已有 1066 人浏览学习,适合作为课程实验报告参考或 SDN 入门实践的配套材料。
1. Mininet 实验报告拆解:一份能照着跑完的 SDN 拓扑实战
做网络实验最烦的不是配协议,而是拓扑根本建不起来。Mininet 看着轻量,真正上手时大部分人卡在同一个地方:miniedit 里画好的拓扑导不出能跑的脚本,或者脚本能跑但 ping 不通。这份实验报告的价值在于把「可视化建拓扑、命令行建拓扑、Python 脚本建拓扑、链路性能限制、节点通信验证」这条链路完整走了一遍,每一步都给了能直接抄的参数和命令,而且最后还落到了 OpenFlow 与 SDN 场景上——它不只是一份交差的作业,更是一份能复现的实验室操作手册。我正在做网络应用设计类课程实验,或者要在本机快速搭一套可复现的 SDN 测试环境,拿它当前期参照都很合适。后面所有步骤我都按原报告的实验顺序拆开讲,踩过的坑也一并标出来,省得你再走一遍弯路。
2. 三种建网方式怎么选:miniedit 可视化、命令行与交互式 API
Mininet 的设计哲学是「同一张拓扑,三种入口」:图形界面、命令行参数、Python API。三种方式不是重复造轮子,而是分别对应三种使用场景。可视化工具适合演示和教学,能让人一眼看懂拓扑长什么样;命令行适合快速验证,一条命令就能拉起一个指定结构的网络;交互式 CLI 则适合在运行中的拓扑上做增量修改,比如临时加一台主机、挂一条链路,不用重启整个网络。实际做实验时,我一般先用可视化把拓扑结构画出来确认逻辑,再转成命令行或脚本批量复现。这样既能保证理解到位,又能把实验过程留给后面复用。
2.1 miniedit 可视化:适合理解拓扑,但不适合批量实验
miniedit 是 Mininet 自带的 Tkinter 图形前端,底层仍然是调用 Mininet 的 Python API,只是把 addHost、addSwitch、addLink 这些操作变成了鼠标拖拽。它的价值在于把抽象的网络结构可视化,尤其适合刚接触 SDN 的人搞清楚「控制器、交换机、主机、链路」四者之间的关系。
按原报告的流程,在 Linux 环境里启动 miniedit 大概分这么几步:
- 进入 root 模式或确保当前用户有 sudo 权限。
- 定位 miniedit.py 文件。常见位置是
/usr/lib/python2.7/site-packages/mininet/examples/或 Mininet 源码目录下的examples/,直接用find / -name miniedit.py 2>/dev/null找最稳妥。 - 启动:
sudo python miniedit.py。注意这里用 python 还是 python3,取决于你的 Mininet 装在哪个解释器下。 - 在画布上添加 Host、Switch、Controller,用连线工具把它们连起来。
- 右键节点配置参数:控制器填 127.0.0.1,交换机选 OpenFlow 协议版本,主机配置同一网段的 IP。
- 在 Edit → Preferences 里勾选 Start CLI,这样启动网络后会自动进入 Mininet 命令行。
- 通过 File → Export Level 2 Script 保存为 Python 脚本,再用
sudo python xxx.py运行重现拓扑。
提示:miniedit 导出的 Level 2 Script 只包含拓扑结构和基本 IP 配置,不包含你在 CLI 里后续做的动态操作。想保留那些操作,必须写进脚本里。
控制器配置这一步最容易忽略。如果拓扑里放的是「None」型控制器,Mininet 不会启动控制器进程,交换机就处于没有控制器的待机状态,数据包会被丢弃。如果只是在本机做连通性测试,选默认 Controller 并填 127.0.0.1 即可;如果要用外部控制器,选 None 后再手动启动。
2.2 命令行创建:把拓扑结构变成参数
命令行的核心是--topo参数,语法为类型,参数1,参数2。原报告里给了三种典型结构,我按可复现的顺序重排如下:
# 最简拓扑:1 个交换机下挂 2 台主机 sudo mn --topo single,2 # 线性拓扑:4 个交换机串联,每个交换机下挂 1 台主机 sudo mn --topo linear,4 # 星形拓扑:3 台主机全部接到同一个交换机 sudo mn --topo single,3 # 树形拓扑:深度 2、扇出 2,共 3 台交换机、4 台主机 sudo mn --topo tree,depth=2,fanout=2single,2是整个 Mininet 里最简单的结构,s1 连接 h1 和 h2,适合验证基本连通性。linear,4是链式结构,s1-s2-s3-s4 依次串起来,每个交换机挂一台主机,数据从 h1 到 h4 要经过多跳,便于观察多跳转发行为。tree,depth=2,fanout=2是二叉树结构,根交换机下挂两台子交换机,每台子交换机再挂两台主机,层级关系更接近真实网络。这三种拓扑分别对应了「汇聚」「链式」「分层」三种常见组网形态,做课程实验时建议三种都跑一遍。
进入 CLI 后用net看拓扑连线、dump看节点 IP 和进程 PID、pingall做全网连通性测试。这些命令是后面所有验证的基础。
2.3 交互式接口:在运行中的网络上动态改拓扑
命令行创建完拓扑后,如果还想加节点,不用退出重建,直接在 CLI 里用py前缀执行 Python API。原报告演示的就是这个操作:
sudo mn mininet> py net.addHost('h3') mininet> py net.addLink(s1, net.get('h3')) mininet> py s1.attach('s1-eth3') mininet> py net.get('h3').cmd('ifconfig h3-eth0 10.0.0.3/24') mininet> h1 ping -c1 10.0.0.3py net.addHost('h3')只是在对象层面上新建了一台主机,此时它还没接入网络;addLink(s1, net.get('h3'))创建了 h3 到 s1 的虚拟链路,但 Open vSwitch 的端口还没有真正启用;s1.attach('s1-eth3')才是把交换机端口挂载上,这一步漏掉的话链路是断的。最后用cmd()在 h3 内部执行 ifconfig 分配 IP,然后从 h1 ping 新主机验证连通。
再强调一个容易抄错的点:原报告里写的是from mininet.until import dumpNodeConnections,实际模块名是mininet.util,until是笔误。用py dumpNodeConnections(net.hosts)或py net.pingAll()也能达到同样效果。这种细节不亲自敲一遍代码很难发现,后面避坑章节我还会专门讲。
3. 用 Python 脚本构建拓扑:从 single/linear/tree 到自定义网络
命令行能覆盖的拓扑类型有限,一旦要定义环形、冗余链路、跨网段隔离这类结构,就必须用 Python 脚本。Mininet 官方推荐的方式是定义 Topo 子类,在build()方法里声明节点和链路,然后交给 Mininet 类去启动。这种方式是声明式的,拓扑结构一目了然,而且天然支持参数化——同一个类可以生成不同规模的网络。
3.1 Topo 类与 Mininet 对象:脚本建网的底层逻辑
写脚本前先搞清楚两个概念。Topo类是拓扑蓝图,只描述「有哪些节点、节点间怎么连」,不实际创建进程和网卡;Mininet类才是执行者,拿到 Topo 后调用start()才真正拉起主机、交换机、控制器。第三方的实验脚本经常把两者混在一起用,在build()里就去启动网络,这样会导致拓扑定义和运行逻辑纠缠,后面想改参数非常痛苦。我的习惯是:build()里只做节点和链路声明,运行时逻辑全放在main()里。
这里再补充一个底层原理。Mininet 里的每台 host 是一个 network namespace,交换机是 Open vSwitch 的用户态进程,host 与 switch 之间通过 veth pair 虚拟网线连接。addHost(ip='10.0.0.1/24')等价于创建命名空间并配置网卡地址,addLink(h1, s1)等价于拉了一根 veth 线并把两端分别塞进 host 和 switch。理解这个模型后,脚本里那些看似抽象的对象就都有了实体对应。
3.2 四种拓扑的完整脚本:linear、single、tree 与单交换机双主机
先看最常见的 linear 拓扑。我们定义 4 台交换机串联、每台各挂 1 台主机:
#!/usr/bin/env python from mininet.topo import Topo from mininet.net import Mininet from mininet.cli import CLI class Linear4(Topo): def build(self): prev = None for i in range(1, 5): s = self.addSwitch('s%d' % i) h = self.addHost('h%d' % i, ip='10.0.0.%d/24' % i) self.addLink(h, s) if prev: self.addLink(prev, s) prev = s if __name__ == '__main__': topo = Linear4() net = Mininet(topo=topo) net.start() CLI(net) net.stop()prev变量记住前一个交换机,循环到第 2 台交换机时把prev和当前交换机连接,就形成了链式结构。IP 用10.0.0.x/24保证所有主机在同一广播域,后面 ping 时不需要额外配路由。CLI(net)会进入交互式命令行,让你像命令行方式一样执行 pingall、dump,net.stop()在退出时统一清理进程,避免残留 OVS 配置。
single 拓扑更简单,一台交换机承接所有主机:
class Single3(Topo): def build(self): s1 = self.addSwitch('s1') for i in range(1, 4): h = self.addHost('h%d' % i, ip='192.168.1.%d/24' % i) self.addLink(h, s1)tree 拓扑则适合用递归实现,深度和扇出都可以参数化。Mininet 其实自带TreeTopo(depth=2, fanout=2),直接用内置类更省事,但理解递归写法有助于处理更复杂的分层网络:
from mininet.topo import TreeTopo if __name__ == '__main__': topo = TreeTopo(depth=2, fanout=2) net = Mininet(topo=topo) net.start() CLI(net) net.stop()如果只是想快速搭一台交换机挂两台主机并手动分配 IP,不写 Topo 子类也完全可以。Mininet 支持命令式实例化:
from mininet.net import Mininet from mininet.node import Controller from mininet.cli import CLI net = Mininet() c0 = net.addController('c0', controller=Controller, ip='127.0.0.1') s1 = net.addSwitch('s1') h1 = net.addHost('h1', ip='10.0.1.1/24') h2 = net.addHost('h2', ip='10.0.1.2/24') net.addLink(h1, s1) net.addLink(h2, s1) net.start() CLI(net) net.stop()显式声明控制器对象是一种好习惯,虽然 Mininet 在缺省情况下会自动拉起一个 Controller,但显式写清楚控制器 IP 和类型后,排查问题时能少一半疑虑。ip='10.0.1.1/24'在 addHost 阶段就完成了网卡 IP 配置,比在 CLI 里再敲 ifconfig 干净得多。
3.3 脚本方式和命令行方式的差异:什么时候该写脚本
脚本和命令行的边界并不绝对,但有几个实用的判断标准。命令行sudo mn --topo single,3能覆盖的实验,写脚本反而增加心智负担;但一旦涉及性能参数、多控制器、自定义路由策略、批量跑自动化测试,命令行就不够用了。脚本的优势在于可复现和可参数化,同一份代码改一个数字就能生成 8 台主机或者 16 台交换机,这是命令行做不到的。
miniedit 导出的 Level 2 Script 本质上也是 Topo 子类,只是把拖拽操作变成了代码。所以「可视化设计 → 导出脚本 → 修改参数 → 批量复现」是最高效的工作流。原报告这一步的流程非常完整,照着做一遍,三种建网方式的关系自然就清楚了。
4. 给链路加约束:TCLink 性能参数与验证方法
拓扑建通只是第一步,真实网络里的带宽、延迟、丢包都是需要模拟的指标。Mininet 里做这件事的关键是 TCLink——它会在链路两端的虚拟网卡上挂 Linux 流量控制规则,用 netem 模拟延迟和丢包,用 tbf 或 hfsc 限制带宽。如果不指定 TCLink,Mininet 默认使用的是普通 veth 链路,带宽跑满宿主机网卡,延迟几乎为零,跟真实网络完全是两码事。
4.1 TCLink 的作用:bw、delay、loss 背后的流量控制原理
TCLink 的常用参数有四个:bw是带宽,单位 Mbps;delay是单向延迟,单位毫秒,需要写成字符串形式如'10ms';loss是丢包率,单位是百分比,取值范围 0 到 100;max_queue_size是最大队列长度,单位是包数。需要注意的是delay是单向延迟,ping 的 RTT 大约是它的两倍加处理时延;loss是单向丢包率,双向测试时实际丢包率会叠加。
原理层面,bw=10会在网卡上挂一个 tbf 队列规则,超过 10 Mbps 的流量被丢弃或排队;delay='10ms'则用 netem 给每个包加上 10ms 的发送延迟。这两个规则是独立的,所以一个链路可以同时限制带宽和增加延迟。写脚本时用link=TCLink全局参数一次性启用:
from mininet.link import TCLink from mininet.net import Mininet from mininet.cli import CLI net = Mininet(link=TCLink) h1 = net.addHost('h1') h2 = net.addHost('h2') s1 = net.addSwitch('s1') net.addLink(h1, s1, bw=10, delay='10ms', loss=1, max_queue_size=100) net.addLink(h2, s1, bw=10, delay='10ms', loss=1, max_queue_size=100) net.start() CLI(net) net.stop()这里最容易犯的错是只在addLink里写了 bw,却忘了在Mininet(link=TCLink)指定链路类型。addLink 的带宽参数在默认链路类型下会被静默忽略,不报任何错,但限速就是不起作用。我拆这类实验报告时,发现至少一半「带宽限制不生效」的问题都出在这。
4.2 写一个带性能限制的脚本并用 iperf 验证
链路约束配置好之后,验证才是重头戏。用 ping 测延迟,用 iperf 测吞吐,用 ping 的统计信息看丢包。进入 CLI 后这样操作:
mininet> h1 ping -c 5 h2 mininet> h1 iperf -s -p 5555 & mininet> h2 iperf -c h1 -t 10 -p 5555ping 的输出里,rtt 平均值应该稳定在 20ms 上下,对应单向 10ms 延迟的往返。iperf 的结果会显示实际吞吐量,如果bw=10设置正确,吞吐曲线应该被压在 10 Mbps 附近;如果看到几百 Mbps 甚至上 Gbps,基本可以断定 TCLink 没生效。
注意:iperf 短连接测出来的带宽往往偏高,因为 tbf 允许一定程度的突发。测速时间至少拉到 10 秒,或者用 UDP 模式指定带宽后观察 loss,结果才可信。
还有个细节:有些最小化安装的 Linux 并没有预装 iperf,需要先apt-get install iperf。Mininet 自带的 iperf 有些版本较旧,如果两端都要用 iperf3,务必保持一致,否则握手会失败。
4.3 别忘了主机的 cpu 参数:CPULimitedHost 与 cgroups
原报告里提到addHost()可以对主机 CPU 进行设置,实现方式如下:
from mininet.node import CPULimitedHost from mininet.net import Mininet from mininet.cli import CLI net = Mininet(host=CPULimitedHost, link=TCLink) h1 = net.addHost('h1', cpu=0.5) h2 = net.addHost('h2', cpu=0.5) s1 = net.addSwitch('s1') net.addLink(h1, s1) net.addLink(h2, s1) net.start() CLI(net) net.stop()注意cpu参数是 0 到 1 之间的小数,cpu=0.5表示该主机最多使用单核 CPU 的 50%,不是 50%。原报告里写的是「以百分数的形式」,这容易误导,实际代码里写cpu=50会导致 cgroup 配额计算异常。原理是 CPULimitedHost 通过 cgroup 的 cpu 子系统限制主机内所有进程的 CPU 占用率,需要 root 权限才能生效。验证方法是在 CLI 里跑一个死循环进程,然后开另一个终端用 top 看 CPU 占用,理论上会稳定在 50% 附近。
5. 避坑指南:五个必踩问题与排查顺序
这份实验报告本身步骤完整,但照着重跑一遍,有几个地方几乎是必踩。我按出现频率排序写在这里,每条都给出了现象、原因和解决方式,实操时可以按顺序排查。
5.1 坑一:miniedit 启动报错,文件到底在哪
现象:运行sudo python miniedit.py直接提示ModuleNotFoundError,或者找不到这个文件。原因:Mininet 安装路径不统一,新版 Mininet 把 miniedit 放在了examples目录,但 PYTHONPATH 里没有包含它;另外不同的发行版安装目录差异很大,盲目用绝对路径自然失败。解决:先执行find / -name miniedit.py 2>/dev/null定位文件;如果报模块缺失,进入文件所在目录运行sudo python miniedit.py;仍然报错就手动export PYTHONPATH=$PYTHONPATH:/usr/lib/python2.7/site-packages/mininet/examples。这个坑本身不复杂,但卡住的概率极高,我建议第一步就用 find 定位,别凭记忆硬猜路径。
5.2 坑二:导出脚本跑不起来,python2/python3 与路径问题
现象:miniedit 里保存的脚本,运行时报ImportError: No module named 'mininet'或者语法错误。原因有两个层面:一是 Mininet 装的是 python2 版本,而系统默认 python 是 3.x,代码用 python2 语法写但被 python3 解释执行;二是导出脚本里的 import 路径依赖安装目录,换了一台机器后解释器找不到包。解决:统一用sudo python执行而不是python3;如果是在自己笔记本上复现,装完 Mininet 后先把mn --version跑通再运行脚本;脚本开头加上#!/usr/bin/env python并确认 PATH 里优先的 python 指向正确解释器。
5.3 坑三:pingall 不通,问题多半在控制器与 IP 网段
现象:启动拓扑后执行pingall,结果出现大量 dropped packets,或者全网全丢。原因:80% 的情况是控制器没配上。miniedit 里如果把控制器类型选成 None,交换机处于无控制器状态,不会转发任何数据包;另一种情况是主机 IP 不在同一网段,导致 ARP 解析失败。解决:先执行dump看每个节点的 IP 和 PID,确认 controller 进程存在;再执行net看链路是否完整;如果控制器正常,检查各主机 IP 掩码是否一致。还想深挖的话,在 OVS 侧执行ovs-vsctl show看 controller 是否连接成功,再ovs-ofctl dump-flows看流表是否下发了规则。这一步能把「拓扑建好但业务不通」的问题缩小到可定位的范围。
5.4 坑四:带宽限制不生效,TCLink 没挂上
现象:addLink里明明写了bw=10,iperf 测出来的带宽却是几百兆甚至上千兆。原因:Mininet 默认链路类型不是 TCLink,bw、delay、loss在默认 IntfLink 下全部被忽略,而且不报错。这是这份实验脚本里最隐蔽的一个坑,因为拓扑能正常通信,只是限速失效,容易被当成「网络质量不好」而错过真正原因。解决:实例化改为Mininet(link=TCLink);或者更直接一点,在 CLI 里执行py h1.cmd('tc qdisc show dev h1-eth0'),如果没有任何 qdisc 输出,说明流量控制规则没挂上。还可以搭配py net.pingAll()和 iperf 做对照,确认规则生效后再看吞吐数据。
5.5 坑五:xterm 与 Wireshark 打不开,图形环境缺失
现象:在 CLI 里输入xterm h1后终端闪退或无响应;wireshark 启动提示无法连接显示器。原因:Mininet 的 xterm 和 Wireshark 依赖图形环境和 X11。如果是在远程服务器上通过 SSH 跑实验,只有一个纯命令行会话,DISPLAY 环境变量是空的,图形程序根本起不来。解决:本机图形桌面直接跑没问题;远程环境先apt-get install xterm,再用ssh -X登录;如果不想折腾图形转发,就放弃 Wireshark,改用命令行抓包工具tshark或tcpdump,在 CLI 里执行h1 tcpdump -i h1-eth0 -c 20一样能看到流量。这个坑不涉及 Mininet 本身,但会直接影响实验截图,做报告时尤其尴尬。
6. 把实验浓缩成一条命令:参数化拓扑与验收技巧
前面拆了那么多步,其实 Mininet 本身提供了一个「一行命令拉起完整实验」的入口。信息密度最高的写法是:任意拓扑结构加上流量控制参数、协议版本、自动 MAC 分配,全部塞进sudo mn命令。
sudo mn --topo=tree,depth=2,fanout=2 \ --link=tc,bw=10,delay=5ms,loss=1,max_queue_size=100 \ --switch=ovsk,protocols=OpenFlow13 \ --mac这条命令把第 3 章的拓扑定义和第 4 章的性能限制合并成了一句话。--link=tc等价于脚本里的TCLink,后面的bw=10,delay=5ms,loss=1全部作用于每一条链路;--switch=ovsk,protocols=OpenFlow13显式指定使用 Open vSwitch 并启用 OpenFlow 1.3;--mac让每个 host 自动分配固定的 MAC 地址,避免 ARP 表中的地址解析问题。进入 CLI 后按下面这张表验收:
| 验证项 | 命令 | 预期结果 |
|---|---|---|
| 拓扑结构 | net | 节点与链路和设计一致 |
| 全网连通 | pingall | 0 dropped |
| 链路延迟 | h1 ping -c 5 h2 | rtt 均值约等于 2 倍 delay |
| 链路带宽 | h1 iperf -s -p 5555 &再h2 iperf -c h1 -t 10 -p 5555 | 吞吐接近 bw 设定值 |
| 节点状态 | dump | 每个 host 都有 IP、MAC、PID |
这一套流程很适合用来给课程实验报告留档:进入 CLI 后先net截一张拓扑图,pingall截一张连通性结果,iperf截一张带宽数据,三张图基本就能说明实验完成了。也是因为有了这条命令,我后来做任何网络仿真实验,都习惯先跑一遍这条完整链路,确认拓扑和限速都正常,再往里面加控制器逻辑。以前总是先写几百行脚本再调试半天,翻车了好几次;那以后我强制自己先验证后扩展,确认基础环境没问题再叠加功能。这份实验报告帮你把该踩的坑都标好了,直接拿它当模板往下做就行,希望帮到你。
本文还有配套的精品资源,点击获取