news 2026/10/6 7:11:17

Mininet实验报告指南:网络模拟、链路参数与SDN控制器对接

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Mininet实验报告指南:网络模拟、链路参数与SDN控制器对接

简介:这是一份完整的 Mininet 网络仿真实验报告,源自西安财经大学《网络应用设计与系统集成》课程实验一,适合计算机网络、SDN 方向学习者对照练习。报告从实验目的出发,依次梳理了基础技能与进阶技能:既包含 Miniedit 可视化工具创建网络拓扑、命令行直接搭建拓扑、交互式界面创建主机和交换机、节点间 ping 测试;也包含通过 Python 脚本构建 linear、single、tree 等常见拓扑,并对主机 CPU、链路带宽、延迟、队列大小、丢包率等网络性能参数进行限制。文档还对 Mininet 的背景、特性及常用命令作了说明,并分步骤呈现控制器配置、交换机配置、主机配置、全局配置、拓扑保存、运行与停止等完整操作流程。资源为单个 doc 文档,大小 1.37MB,内含详细步骤和界面截图说明,便于随时查阅复现。已有 1066 人学习浏览,适合正在完成 Mininet 实验、准备实验报告或复习网络仿真要点的同学参考。

1. Mininet实验报告,到底在解决什么问题

当你在网络课、SDN项目或者性能测试里需要一组“网络设备”时,最直接的方案是搬出几台物理交换机、路由器和服务器,接线、配置、抓包。但大多数时候,你手里只有一台笔记本,而且要复现的拓扑可能是三层交换机加五台主机,或者一个环形链路。这时候Mininet就派上用场了。它是一个基于进程虚拟化技术的网络模拟器,能在单台Linux机器上用命名空间模拟出真实的主机、交换机、链路,并且在上面跑真实的网络协议栈和应用程序。Mininet实验报告的核心价值,就是让你在几秒钟内搭建出一张可编程的虚拟网络,并且用iperf、ping、抓包工具去测量它——结果能反映真实网络的大部分行为,又不需要采购任何硬件。这篇笔记专门开给三类人:正在做网络实验作业的学生、要验证OpenFlow流表行为的SDN开发者、以及需要做链路断线和延迟抖动测试的运维工程师。

2. 跑通第一个Mininet拓扑:安装、最小命令与连通性验证

2.1 为什么选Mininet而不是GNS3或EVE-NG

先回答一个常见疑问:GNS3和EVE-NG也能搭虚拟网络,为什么Mininet更适合实验?因为Mininet不是去模拟整个硬件,而是用Linux的network namespace(网络命名空间)把一台真实内核拆成多套独立的网络栈。每个host是一个单独的命名空间,有自己的网卡、路由表和ARP缓存,而交换机用软件实现的虚拟交换设备(默认是Open vSwitch或用户态交换机)来模拟。这意味着你在Mininet里跑的TCP/IP协议栈、socket API、ping命令,全部是内核原生的,不是重新实现的模拟器逻辑。这也是为什么Mininet实验的结果,比如带宽测试,往往比GNS3的QEMU虚拟机方案更接近真实。当然代价是,它只能在Linux上运行(Windows需要WSL2或虚拟机),而且不能模拟硬件转发延迟,部分物理层行为会失真。对于做协议验证和SDN开发,这个取舍是值得的。

安装方式常见有两种:一种是通过apt直接装,适合快速开始;另一种是从源码安装,适合需要修改Mininet自身或想用最新版的情况。我一般建议第一次直接apt装,版本旧一点但稳定。

# 方式一:apt安装(Ubuntu/Debian) sudo apt update sudo apt install -y mininet # 方式二:源码安装(获取最新开发版) git clone https://github.com/mininet/mininet cd mininet sudo util/install.sh -a

逻辑说明:方式二中的install.sh -a会安装Mininet及其依赖的Open vSwitch、Wireshark等工具,适合打算后续做SDN实验的人。如果只跑基础拓扑,方式一就够了。参数说明:-a表示all,包括控制器、交换机和可视化组件;如果网络环境不好,可以改用-nf只装Mininet本体和Open vSwitch。

2.2 最小拓扑:一个交换机加两主机的连通性验证

安装完成后,第一个实验建议用默认的miniedit或者最简单命令来创建一个交换机+两个主机的拓扑。Mininet自带一个命令mn,它把创建进程、分配IP、启动交换机这几件事全封装了。我习惯先用--test pingall跑一次完整校验,再手动进入交互环境。

sudo mn --topo single,2 --mac --switch ovsk --controller none

参数说明:--topo single,2表示用single拓扑(即一个交换机),后面跟2个主机;--mac让主机的MAC地址固定为00:00:00:00:00:01这样的格式,便于抓包分析时识别;--switch ovsk指定使用Open vSwitch作为虚拟交换机,这样后续对接OpenFlow控制器时更顺手;--controller none表示不启动控制器,交换机工作在普通二层转发模式。这个命令执行后会进入mininet>提示符,此时可以运行pingall看连通性。

mininet> pingall

如果输出是host0 -> host1 OK这类结果,说明两个主机之间的二层通路已经打通。这里有个容易被忽略的点:--controller none时,Open vSwitch默认会学习MAC地址并转发,所以ping能通。如果你想验证这一层,可以再执行dpctl dump-flows查看流表。

2.3 手动验证链路与链路属性

拓扑建好之后,别急着开iperf。先用net命令看一下每个节点的网络接口和IP分配,再确认链路是否按预期工作。

mininet> net mininet> intfs mininet> links

三个命令的作用分别是:net列出host、switch以及它们之间连接关系;intfs显示每个接口的详细参数;links只看链路状态。多数新手会跳过这一步,直接跑应用,结果带宽测试失败后开始怀疑Mininet,其实是接口没配对。我一般会用xterm打开一个host的终端,手动ping对端,这样能同时看到两端进程的输出。

如果连默认拓扑都没跑通,优先检查两个东西:第一,虚拟化支持是否开启,用egrep -c '(vmx|svm)' /proc/cpuinfo看一下;第二,是否有残留的虚拟网卡或者老进程占用资源,用sudo mn -c清理一次再试。mn -c这个命令可以理解为后悔药,它会把上次实验留下的namespace、网桥、临时文件全部清掉,避免脏环境。

2.4 用--link参数初步设置延迟与丢包

Mininet的命令行允许直接给链路附加模拟参数,这是它区别于普通虚拟机网络的关键能力。比如执行:

sudo mn --topo linear,3 --link tc,delay=20ms,loss=10

这个命令创建一条线性拓扑(switch1连host1,switch1连switch2,switch2连host2,以此类推),并对每条链路叠加tc(Linux流量控制)配置:单向延迟20毫秒、单向丢包率10%。注意这里tc是Mininet link类型的一种,底层调用TC工具在虚拟接口上挂载netem队列。loss=10表示每一跳随机丢包10%,不是一次实验丢10%的包。

为什么要在命令行里先试这个?因为你可以立刻用ping验证效果:pingall会显示部分丢包,ping -c 10能算出平均RTT大约40毫秒(因为双向)。这个结果非常直观,让你确信任意链路参数的修改真实生效了。但也别急着把所有参数都堆到命令行里,因为一旦拓扑复杂,链路参数混在一起后期非常难维护。更合理的做法是回到Python脚本,用addLink的bw、delay、loss参数显式声明链路,这正好是下一章的主题。

Mininet的链路参数底层都交给Linux TC实现。delay=20ms会转换成tc qdisc add dev ... root netem delay 20ms,loss=10对应netem loss 10%,bw则使用tbf或htb队列。了解这一点对你排错很有用:当你看到Mininet里的链路表现不符合预期时,直接进到host的接口上看qdisc配置。例如:

mininet> sh tc qdisc show dev s1-eth1

这条命令会展示s1连接h1的那张网卡上已经挂载的队列规则。如果输出里没有netem或tbf,说明链路参数没有生效。这个底层视角能让你的实验报告更有说服力,也更容易解释为什么loss的随机性会影响数据。很多教材只教你用Mininet命令,不告诉你TC层发生了什么,导致你遇到问题时无从下手。

2.5 最小实验报告:数据怎么记才有效

为了让你之后写实验报告不抓狂,我建议从第一个拓扑开始就养成记录习惯。Mininet本身不生成报告,但你可以用命令输出重定向保存现场数据。例如:

sudo mn --topo single,2 --mac --switch ovsk --controller none --test pingall > result.txt 2>&1

这里--test pingall会让mn直接运行测试后退出,不进入交互模式。> result.txt 2>&1把标准输出和错误都写到文件。这样做的好处是,你得到的实验证据可以粘贴到报告里,而不是靠回忆。注意--test模式下,Mininet会套用默认的iperf等其他测试选项,如果你只想要ping结果,这个写法是对的。2>&1这个重定向在Linux上极其常用,但很多新手只写>,结果报错信息丢了一堆。

报告里除了命令输出,我一般还会记录一个表格:

检查项命令预期结果
拓扑连通性pingall所有host间OK
RTTpy net.hosts[0].cmd('ping -c 3 10.0.0.2')丢包率0%,RTT平均<0.1ms
流表dpctl dump-flows存在两条转发流表项

这个表并不是凑字数,它是实验报告的核心骨架。很多同学写Mininet实验报告,直接贴一张截图,截图里只有一个pingall的OK,这等于把黑匣子原样交给了老师。有了表格和对应的命令输出,别人复现你的实验时才知道要跑哪些命令、期待什么结果。后续的带宽、延迟实验也可以沿用同一套表格模板。

3. 自定义拓扑实验:用Python脚本控制带宽、延迟与丢包

3.1 为什么需要Python脚本而不是mn命令

mn命令可以快速演示,但真实实验场景里拓扑往往是分层的、链路参数需要逐条指定,甚至需要动态修改。这时候就得写Mininet的Python API脚本。它本质上是一段普通的Python程序,通过导入mininet库,定义Topo子类或直接使用Topo对象来构建网络。相比命令行,Python脚本的好处有三个:第一,参数可以用变量和循环生成,比如批量创建20个主机、按矩阵连接交换机;第二,可以注册启动后的回调,比如在start()后自动设置QoS;第三,实验报告可以嵌在代码注释里,让脚本本身变成可复现的文档。

一个最小但完整的脚本如下,你把它存成my_topo.py,然后sudo python3 my_topo.py就能运行。

#!/usr/bin/env python3 from mininet.topo import Topo from mininet.net import Mininet from mininet.node import OVSSwitch, RemoteController from mininet.cli import CLI from mininet.link import TCLink class MyTopo(Topo): """自定义拓扑:s1连接h1/h2/h3,s1-s2级联,s2连接h4""" def build(self): # 创建交换机 s1 = self.addSwitch('s1', cls=OVSSwitch) s2 = self.addSwitch('s2', cls=OVSSwitch) # 创建主机 h1 = self.addHost('h1', ip='10.0.1.1/24') h2 = self.addHost('h2', ip='10.0.1.2/24') h3 = self.addHost('h3', ip='10.0.1.3/24') h4 = self.addHost('h4', ip='10.0.2.4/24') # 添加链路,指定带宽、延迟、丢包 self.addLink(s1, h1, bw=10, delay='5ms', loss=0, use_htb=True) self.addLink(s1, h2, bw=20, delay='10ms', loss=1) self.addLink(s1, h3, bw=30, delay='15ms', loss=0) self.addLink(s1, s2, bw=100, delay='2ms', loss=0) self.addLink(s2, h4, bw=50, delay='20ms', loss=0) if __name__ == '__main__': topo = MyTopo() net = Mininet(topo=topo, switch=OVSSwitch, link=TCLink, controller=RemoteController) net.start() print("链路状态:") for link in net.links: print(link) CLI(net) # 进入交互命令行 net.stop()

逻辑说明:这个脚本定义了一个继承Topo的类,在build()方法里添加节点和边。每个主机显式指定了IP,这样可以避免Mininet默认的10.0.0.x分配不够用。self.addLink的前两个参数是端点,后面跟的关键字参数都是链路属性:bw单位是Mbit/s,delay可以是'5ms'这样的字符串,loss是整数百分比,use_htb=True表示带宽限制使用HTB队列规则,默认就是True,有时候写出来是提醒读者这里有队列调度。在main部分,用Mininet类加载拓扑,指定交换机和链路类型。RemoteController是远程控制器的占位,如果你还没有启动控制器,可以先用Controller(默认)代替,这里写RemoteController只是为了展示如何在后面对接SDN。

参数说明需要重点理解:link=TCLink是必须的。如果漏掉这一行,Mininet会使用Link类,它只做数据通路连接,完全不施加任何带宽/延迟/丢包限制。你会在实验时发现bw=10根本不生效,所有链路跑满千兆。这是新手最容易踩的坑之一。另一个细节是,delay和loss只对TCLink有效,而bw如果不指定,默认没有限制。

3.2 如何验证链路参数真实生效

脚本写好后,光靠ping看不出带宽限制。跑一下iperf是最直观的验证方式。在CLI里执行:

mininet> iperf h1 h2

但iperf命令默认只测TCP,且输出只有带宽值。我建议用更详细的模式:进入h1的xterm,手动启动iperf3 -s,然后在h2里执行iperf3 -c 10.0.1.1 -t 10。这样你可以观察限速效果。比如h1的bw是10Mbps,h2的bw是20Mbps,那么h1到h2的方向,实际吞吐应该被限制在10Mbps附近。这里有个关键现象:由于TCP拥塞控制,实测带宽会接近但略低于9Mbps,因为还有协议头部开销。这不是Mininet的bug,而是TCP对链路容量的正常收敛。

如果你想同时验证延迟和丢包,在CLI里运行:

mininet> py net.hosts[0].cmd('ping -c 10 10.0.1.2')

观察输出里的丢包率。比如loss=1时,10个包丢1个,通常输出9 received, 1% packet loss。但要注意netem丢包是概率性的,可能10次刚好0丢包,也可能丢2个,所以实验报告的丢包率应该用-c 100甚至-c 1000来统计,才能逼近设定值。我在做延迟验证时,会额外注意RTT值:延迟是单向参数,ping结果是双向RTT。所以delay=5ms的链路,ping RTT大约在10ms左右(加上处理延迟约0.05ms)。很多人报告里写RTT 5ms,其实那是理解错了方向。

3.3 动态调整链路参数:不用重建拓扑的实验技巧

Mininet的另一个好处是可以在运行中修改链路参数,而不需要重启整个拓扑。在CLI里执行:

mininet> link s1 h1 loss 20 mininet> link s1 h1 delay 20ms

link命令接受loss、delay、bw这三个关键词,后面跟新值。这个操作本质上是重新配置TC的netem和tbf规则。对于做断线实验,可以直接用link s1 h1 down和link s1 h1 up模拟物理链路故障和恢复。我当年做链路切换实验,就是靠这个命令反复触发交换机重新学习拓扑,省去了重启拓扑的等待时间。注意,当你用link命令修改了参数后,如果想恢复初始值,必须显式写回,比如link s1 h1 loss 0。CLI不会记住初始配置。

动态调整还有一层含义:你可以写一个后台Python线程,周期性地改变某条链路延迟,用来模拟网络抖动。这在验证实时流媒体协议时很有用。但要注意线程与Mininet事件循环的同步,不能直接在一个线程里调用net对象,建议通过net.monitor或timer来实现。

3.4 实验报告的带宽数据怎么记录

跑完带宽实验,粗心的人只记一个最终带宽。但实验报告需要过程的波动情况。用iperf3 -c 10.0.1.1 -t 10 -i 1可以让iperf每秒输出一次带宽值,你把这些值记录成表格,能看出TCP是否稳定。如果是UDP测试,还需要指定-u -b 100M表示发送速率。比如验证链路限速时,UDP包可能会超速或丢包,这本身就是重要的实验现象。

# 在h1上(服务端) iperf3 -s -p 5002 > server.log & # 在h2上(客户端)发送UDP 50Mbps iperf3 -c 10.0.1.1 -p 5002 -u -b 50M -t 5 -i 1

参数说明:-p指定端口,-u表示UDP,-b 50M表示目标带宽50Mbps,-t 5持续5秒,-i 1每秒打点。如果你在第二条链路上设置了bw=30,那么以50Mbps发送,会观察到接收带宽约30Mbps和约20Mbps的丢包。这个实验直接证明了TC限速真实有效。把打点数据复制进Excel,画一条时间-带宽曲线,就是漂亮的实验报告图表了。

3.5 常用的链路参数实验组合速查表

在写实验报告时,很多人不知道如何选择链路参数来模拟不同场景。我一般用这样一套组合:模拟局域网时,bw=100, delay=1ms, loss=0;模拟跨地域广域网,bw=20, delay=20ms, loss=0.1;模拟无线链路,bw=50, delay=5ms, loss=2;模拟拥塞链路,bw=10, delay=50ms, loss=5。每个组合都建议用iperf和ping同时验证,因为这些参数之间会互相影响(比如延迟大、带宽小时,TCP吞吐受带宽延迟积限制)。

场景bw(Mbit/s)delay(ms)loss(%)验证重点
局域网10010TCP吞吐接近带宽
广域网20200.1单程延迟约20ms,RTT约40ms
无线链路5052丢包率约2%,ping方差大
拥塞链路10505吞吐远低于带宽,丢包明显

这个表不仅帮助你快速实验,还能在报告里展示你对场景与参数映射的理解。如果你做的是对比实验,一定要确保所有场景都跑在同样的拓扑下,只改变链路参数,否则结论就不可信了。

4. 对接SDN控制器:把Mininet变成OpenFlow实验床

4.1 Mininet的控制器类型与选型

Mininet自带一个简单的参考控制器Controller,但它只实现了基本的二层MAC学习,无法满足你对于OpenFlow流表编程的期待。当你开始做SDN实验时,通常需要外接一个远程控制器,比如Ryu、OpenDaylight、ONOS、Floodlight等。Mininet的角色变成交换机侧的工具,它通过OpenFlow协议连接远程控制器,并把包转发行为交由控制器决策。这里的关键点是,Mininet里每个虚拟交换机都是一个OpenFlow交换机实例,默认监听6653端口(旧版本是6633)。如果你没有启动任何控制器,交换机端口是关闭的,除非使用--controller none进入自治二层模式。

控制器选型没有绝对标准。我个人经验:如果做基础教学实验,首选Ryu,因为它是纯Python、代码量小、用ryu-manager一条命令就能启动,配合simple_switch_13.py示例五分钟跑通。如果做生产级多控制器高可用实验,可以选ONOS或OpenDaylight。Floodlight也还有人用,但新项目不多。注意一点,不同控制器版本对OpenFlow协议版本支持不同,Mininet默认会尝试1.3(Open vSwitch 2.7以上),老旧控制器只支持1.0的话需要额外配置。

4.2 远程控制器连接:启动Ryu并让Mininet接入

先给出最标准的三步实验流程。第一步,启动Ryu控制器(需要先安装):pip install ryu。第二步,运行一个最简单的二层转发应用:

ryu-manager --verbose ryu.app.simple_switch_13

--verbose会把控制器收到的Packet-In、流表下发等消息打印到屏幕,这些日志是后面排查问题的重要依据。simple_switch_13是Ryu自带的OpenFlow 1.3二层自学习应用,行为和普通交换机一样,但它通过控制器下发流表来实现MAC学习。第三步,启动Mininet并指定远程控制器:

sudo mn --topo single,3 --controller remote --ip 127.0.0.1 --port 6653 --switch ovs

参数说明:--controller remote告诉Mininet不要用默认控制器,--ip是控制器所在地址,本机就是127.0.0.1,--port是OpenFlow端口。这一步启动后,你会看到Ryu的终端滚动日志,显示“Subscribed the switch”之类的事件。此时执行pingall,理论上应该全通。但如果你跑的是自学习交换机应用,第一次ping的包会触发Packet-In,控制器下发流表,之后就走硬转发。为了确认,在Mininet CLI里执行:

mininet> sh ovs-ofctl dump-flows s1

这条命令会显示交换机s1的流表项。注意:这里的sh表示在Mininet所在Linux主机上执行shell命令,不是进入某个host。ovs-ofctl是Open vSwitch自带的控制工具,参数dump-flows s1表示打印s1的所有流表。如果看到类似table=0, n_packets=…, priority=…, dl_dst=… actions=output:…的记录,说明流表确实由控制器下发。

关于端口参数,Mininet默认的OpenFlow端口是6653,但有些教程还写6633,这是旧版本常值。如果你的Ryu监听的是6633,那么Mininet启动时就要指定--port 6633。我见过很多翻车情况是因为一个默认值不一致,导致控制器日志毫无反应。统一用6653可以减少不必要的麻烦。此外,Ryu应用如果报错No module named ryu.lib.packet之类,多半是pip安装时权限不对或版本不兼容。建议在虚拟环境安装:python3 -m venv venv && source venv/bin/activate && pip install ryu。这样能避开系统Python的权限问题。这套虚拟环境的做法也适用于Mininet的Python API脚本,但注意Mininet本身需要root权限,而venv里的普通用户无法用root运行脚本,所以你需要用sudo python3加上venv的Python路径来启动。

4.3 用自定义控制器实现一个简单的静态流表实验

如果你不想依赖自学习应用,想手动控制转发路径,可以写一个最小的Ryu控制器,只下发一条指定主机到主机的静态流表。这是一个经典实验:让h1和h2互通的流表完全由你指定,禁止其他流量。代码很短:

from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import packet, ethernet class SimpleStatic(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] @set_ev_cls(ofp_event.EventOFPSwitchFeatures, dispatcher=0) def switch_features(self, ev): dp = ev.msg.datapath ofproto = dp.ofproto parser = dp.ofproto_parser # 下发两条流表:h1 -> h2,h2 -> h1 # h1的MAC是00:00:00:00:00:01,h2的MAC是00:00:00:00:00:02 for src_mac, dst_mac, out_port in [ ('00:00:00:00:00:01', '00:00:00:00:00:02', 2), ('00:00:00:00:00:02', '00:00:00:00:00:01', 1), ]: match = parser.OFPMatch( eth_src=src_mac, eth_dst=dst_mac, eth_type=0x0800, ) actions = [parser.OFPActionOutput(out_port)] inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod( datapath=dp, priority=10, match=match, instructions=inst, ) dp.send_msg(mod)

逻辑说明:switch_features事件在交换机连接控制器时触发,我们利用它第一时间下发流表。OFPMatch定义了匹配规则:源MAC、目的MAC、以太网协议类型(IPv4)。OFPActionOutput指定从哪个端口送出。端口号2和1分别对应s1连接h2和h1的端口,这要依赖实际拓扑顺序。你可以先用sh ovs-ofctl show s1查看端口对应关系,再修改代码里的out_port。这段代码的本质是静态路由实验的OpenFlow版,它让你看清一个SDN转发规则需要哪些字段、端口号怎么映射。

跑这个实验时,配合Wireshark抓包会非常直观。在Mininet CLI里启动抓包:sh tcpdump -i s1-eth2 -w /tmp/p.pcap,然后h1 ping h2,再用Wireshark打开pcap文件,能看到ICMP包,而交换机里的其他流量不会产生任何动作。

4.4 控制器连接失败时的排查步骤

控制器没连接上是最常见的翻车场景。现象通常有两种:Mininet启动时没有报错,但pingall全挂;或者Ryu日志里完全没有“Switch features”消息。排查顺序我一般这样:

# 1. 确认控制器进程真的在跑并监听端口 sudo netstat -tlnp | grep 6653 # 2. 确认Mininet的交换机在尝试连接 sudo ovs-vsctl show | grep -A5 Controller # 3. 抓取6653端口流量看是否三次握手成功 sudo tcpdump -i any port 6653 -c 20

如果netstat显示Ryu监听的是9000端口或者其他端口,那就是启动参数不对。如果ovs-vsctl show显示Controller状态为is_connected: false,说明交换机主动连了但被拒绝。常见原因是控制器使用的OpenFlow协议版本与交换机不一致,比如Ryu默认只启用了1.3,而Mininet的Open vSwitch强制协商版本失败。你可以用--switch ovs,protocols=OpenFlow13来指定协议。还有一个容易被忽略的坑:Mininet启动时会以--controller remote连接控制器,但如果你的控制器绑定了--app参数而没有--verbose,日志可能不打印连接消息,并不代表没连上。

5. 避坑指南:Mininet实验里最常翻车的5个细节

Mininet的优点在于轻量,但轻量也意味着它的行为受宿主机环境影响很大。这一章我把过去两年实验里踩过最深的5个坑列出来,每条都按“现象-原因-解决”的顺序写,你在复现自己的拓扑时遇到类似情况,直接对号入座。

5.1 链路参数不生效:设了带宽却跑满千兆

现象:你在Python脚本里写了addLink(s1, h1, bw=10),然后iperf测试h1到h2,带宽显示800Mbps以上。原因几乎都是Mininet构造时没有把链路类型指定为TCLink。在Mininet类的初始化参数里,link默认是Link类,它只是创建一对veth并连接,完全不解析bw、delay、loss这些参数。命令行方式也一样,如果你不带--link tc,mn --topo single,2 --link tc才有TC效果。解决方法是检查你的构造代码是否写了link=TCLink。在CLI里,可以用py命令确认链路对象类型:

mininet> py net.links[0].status() mininet> link s1 h1

link s1 h1如果输出里没有bw字样,说明该链路不是TC管控的。另外需要注意的是,如果你使用了addLink但漏掉use_htb=False(默认False),带宽限制也不会用HTB队列,而是默认使用tbf?实际上Mininet的TCLink默认use_htb=True。这里有个细节:在某些旧版本里,bw参数必须配合use_htb=True才能准确限速,否则可能用的是更简易的tbf,导致限速不精确。建议显式加上use_htb=True。

5.2 环境残留导致启动失败:mn -c是后悔药

现象:第二次运行sudo mn时报错:“Error creating network namespace”或者Open vSwitch数据库冲突。原因:上一次实验的非正常退出(比如Ctrl+\直接kill、关闭了xterm)导致命名空间、虚拟网桥、veth对没有回收。解决:强制清理再重试。

sudo mn -c sudo ovs-vsctl show

mn -c会清理所有Mininet相关的进程、网桥、命名空间、临时文件。执行完后,检查ovs-vsctl show的输出是不是空的,如果还有残存的bridge,可以手动删:sudo ovs-vsctl del-br s1。这个操作我可是用血泪经验换来的,早期我为了图省事,直接重新跑mn,反复报错后才发现是残留问题。现在我的习惯是:写实验脚本前先执行一次mn -c作为保险。

5.3 实验数据抖动太大:别让CPU调度背锅

现象:同一拓扑,连续跑三次iperf,结果分别是950M、780M、870M。原因:Mininet的所有host都是本机进程,CPU调度和中断处理都会影响实时性,尤其是多主机同时跑iperf时,内核栈和netem队列争抢CPU。解决:第一,减少同一时刻并发测试的主机数量,别用iperf命令一次测所有主机,而是逐个测试;第二,给iperf进程足够的时间预热,比如-t 10而不是-t 2;第三,如果你在复用CPU的公用服务器上做实验,建议用taskset把Mininet主进程绑定到某个物理核,避免跨NUMA节点带来额外延迟。这个方法不能完全消除抖动,但能让你在报告中把误差归因到更科学的位置。

5.4 远程控制器连接成功但流表为空

现象:Ryu日志显示交换机已连接,pingall失败,ovs-ofctl dump-flows s1输出空。原因:最常见的是控制器应用没有对Packet-In事件做出响应,或者匹配条件写错。排查步骤:先在Mininet CLI执行pingall,然后立刻在Ryu控制台看有没有打印HTTP或OF消息。如果交换机是回环连接的,没有产生Packet-In,可能是流表规则里有priority=0的table-miss,但它只匹配不上其他规则的包,正常。如果确实有Packet-In但不下发流表,检查你的match字段。我遇到过一个例子:控制器应用用了eth_type=0x0806来匹配ARP,但实际流量是IPv4的ICMP,eth_type=0x0800,导致ARP被正确转发,ICMP全丢。这就是协议号写错导致的流表空转,排查时可以结合Wireshark抓包看控制器的of消息。

5.5 每次结果不可复现:把随机因素摆在明面上

现象:同一条命令ping -c 5 h2,第一次丢包20%,第二次0%。原因:netem的丢包是基于概率的,而且Mininet的ARP响应时间也可能影响前几个包的统计。解决:把测试次数放大到统计稳定的量级。比如测丢包率用:

mininet> h1 ping -c 100 -i 0.2 -q h2

-i 0.2表示间隔0.2秒,-q只输出汇总信息。这样100个包的丢包率能稳定到整数位。延迟同理,用ping -c 100 | tail -1取平均。另外,pingall的结果只适合连通性判断,不适合写进实验报告的丢包率。我一般还会记录每个host的CPU型号或任务数,防止别人质疑数据时无法解释。实验报告里注明“本测试在空闲CPU下进行,重复三次取中位数”,这比数值本身更有说服力。

6. 把实验写成可复现报告:自动化收集结果与拓扑可视化

6.1 用脚本一键完成「拓扑创建 + 测试 + 数据导出」

写实验报告的痛苦往往不是实验本身,而是把数据从各个终端复制出来、粘贴到Word。Mininet提供了--test模式,但你也可以直接在Python脚本里注册自定义测试函数。下面的脚本演示了如何创建拓扑后,自动执行ping和iperf,并把结果写入文本文件。

from mininet.net import Mininet from mininet.node import OVSSwitch from mininet.link import TCLink from mininet.topo import Topo class TwoHostTopo(Topo): def build(self): s1 = self.addSwitch('s1') h1 = self.addHost('h1', ip='10.0.0.1/24') h2 = self.addHost('h2', ip='10.0.0.2/24') self.addLink(s1, h1, bw=10, delay='5ms', loss=0) self.addLink(s1, h2, bw=10, delay='5ms', loss=0) if __name__ == '__main__': topo = TwoHostTopo() net = Mininet(topo=topo, link=TCLink, switch=OVSSwitch) net.start() # 在h2上启动iperf服务端 net['h2'].cmd('iperf -s &') net['h1'].cmd('sleep 1') with open('report.txt', 'w') as f: f.write("=== Ping Test ===\n") f.write(net['h1'].cmd('ping -c 10 -q 10.0.0.2')) f.write("=== IPERF TCP ===\n") f.write(net['h1'].cmd('iperf -c 10.0.0.2 -t 5 -i 1')) net.stop()

逻辑说明:net['h1'].cmd(...)在host节点里执行命令,返回值是命令的标准输出字符串,直接写入文件即可。这里用ping -c 10 -q获得汇总数据,用iperf -c 10.0.0.2 -t 5 -i 1获得每秒带宽。注意iperf默认是客户端模式,所以先在对端h2上启动服务端,给它1秒时间监听,再让h1发起连接。如果漏了服务端启动,iperf -c会直接报“connect failed”,report.txt里只有错误信息。

6.2 导出拓扑图:把拓扑画进实验报告

Mininet自带一些CLI命令,比如net和dump可以列出节点关系,但要出图还得靠Graphviz。一个简单做法是写一个小函数遍历net.links,生成dot格式的拓扑描述:

def dump_topo_to_dot(net): with open('topo.dot', 'w') as f: f.write('graph topo {\n') for host in net.hosts: f.write(f' {host.name} [shape=circle];\n') for switch in net.switches: f.write(f' {switch.name} [shape=box];\n') for link in net.links: f.write(f' {link.intf1.node.name} -- {link.intf2.node.name};\n') f.write('}\n')

然后在shell里用dot -Tpng topo.dot -o topo.png渲染。如果你装了Graphviz。这个技巧能帮你生成和实验一致的拓扑图,比截图命令行清楚得多。注意dot文件里节点名要保持唯一,Mininet的命名规则本来就是s1、h1这种,所以直接可用。

6.3 一份实验报告应当包含的最小要素

我做完实验后,整理报告会固定检查四块内容:一是实验拓扑图和节点参数表,二是连通性验证结果(pingall输出),三是性能测试数据(带宽、延迟、丢包率及测试命令),四是关键流的抓包截图或流表快照。这四个要素缺一不可。如果你是用我这篇文章里给出的脚本来做实验,那么report.txt自动包含了第二、三块,流表快照用ovs-ofctl dump-flows保存,拓扑图用dot脚本生成。所有文件放入一个目录,放在实验报告的同级目录下,方便别人核验。

最后说一个我自己的习惯:我每个实验都会在同一台Linux物理机上跑三次,然后记录CPU占用和空闲内存,再取中位数写进报告。原因是Mininet依赖宿主机的实时调度,任何后台任务都会污染数据。这个习惯帮我挡下了不少“实验结果无法复现”的质疑。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 7:09:33

STM32实战:软件模拟I2C读写AT24C02 EEPROM全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 7:09:29

STM32从上电到RTOS任务切换:复位向量、启动流程与PendSV深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 7:08:33

汇川AM401伺服张力控制系统实战:从硬件选型到PID调试全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 7:08:33

GD32F470开发板原理图深度解析:从电源树到外设设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 7:08:31

PCIe转网口硬件设计的17个生死关键点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 7:08:30

反激变压器设计避坑指南:漏感、气隙与磁饱和实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华