Mininet三种创建方式:可视化拖拽、命令行速建与交互式调试
简介:本资源为高校计算机网络课程配套的Mininet基础实验报告,面向网络工程、软件定义网络(SDN)初学者及实践教学场景,聚焦轻量级网络仿真平台的核心操作能力培养。报告系统覆盖Miniedit可视化建模、命令行拓扑构建、交互式节点配置与Python脚本化拓扑生成四大技能层级,并包含ping连通性测试、控制器/交换机/主机参数配置、带宽与延迟等性能限制实操,助力读者掌握从界面操作到代码开发的完整SDN实验闭环。资源为单个Word文档(.doc),全文16页,含西安财经大学信息学院《网络应用设计与系统集成》课程标准实验记录、详细步骤截图说明、Mininet常用命令速查表及典型拓扑脚本示例,包体大小1.37MB,结构紧凑、即下即用。目前已有1068人学习下载,适合课程实验复盘、课设参考及SDN入门者快速上手实践。
1. Mininet 实验报告不是作业模板,而是 SDN 开发者的第一块“沙盒实验台”
你手头这份叫1_实验1mininet实验报告.doc的文档,表面看是某高校《网络应用设计与系统集成》课的课堂实验记录,但实际它是一份被反复验证过的Mininet 入门实操路线图——不是概念堆砌,不是命令罗列,而是从可视化拖拽、命令行速建、交互式调试,到 Python 脚本化拓扑定义的完整闭环。它解决的不是“怎么交作业”,而是“如何在单机上跑出一个能 ping 通、能抓包、能改带宽、能加延迟、能对接真实 OpenFlow 控制器的微型 SDN 网络”。适合刚接触软件定义网络(SDN)的本科生、转岗网络自动化的新手工程师,以及需要快速验证控制器逻辑的开发人员。别被“实验报告”四个字骗了——里面藏着的miniedit.py导出脚本、--topo linear,4的参数含义、addLink(bw=10, delay='5ms', loss=2)的真实效果,全是能在真实项目里直接复用的硬核能力。我当年第一次用它搭出带 QoS 限制的树形拓扑时,才真正理解什么叫“代码即网络”。
2. Mininet 三种创建方式:可视化、命令行、交互式,哪条路最快上手?
Mininet 不是只能写 Python;它提供了三层抽象:图形界面保底、命令行提效、交互式调试兜底。这三者不是并列选项,而是递进关系——先用 miniedit 拖出直觉,再用命令行验证拓扑语义,最后进 CLI 逐节点调试。下面拆解每种方式的真实操作链、关键参数和底层逻辑。
2.1 可视化工具 miniedit:拖拽即拓扑,但导出脚本才是真价值
miniedit 是 Mininet 自带的 GUI 工具,路径通常为/usr/local/bin/miniedit.py或~/mininet/examples/miniedit.py。它不生成配置文件,而是导出可执行的 Python 脚本——这才是它区别于其他网络模拟器的核心价值:所见即所得,所得即可部署。
启动前需确认环境:
sudo apt update && sudo apt install -y x11-xserver-utils # 确保 X11 转发可用(尤其在 SSH 连接虚拟机时) sudo python3 /usr/local/bin/miniedit.py提示:若报错
No module named 'tkinter',需安装sudo apt install -y python3-tk;若提示cannot connect to X server,请在 SSH 登录时加-X参数(如ssh -X user@vm)。
构建拓扑后,关键配置点有三处:
- 控制器设置:右键空白处 →Controller→ 选择
Remote Controller并填入 IP/Port(如127.0.0.1:6653),这是后续对接 ONOS/Ryu/Floodlight 的入口; - 交换机 OpenFlow 版本:菜单栏Edit → Preferences→ 勾选
Start CLI(启用交互终端),并在OpenFlow Version下拉框中选OpenFlow 1.3(当前主流控制器默认支持); - 主机静态 IP 配置:右键主机 →Properties→ 在
IP Address栏填10.0.0.1/24类格式(注意带子网掩码/24,否则ifconfig不生效)。
导出脚本必须选File → Export Level 2 Script(非 Level 1),Level 2 会包含addLink()、addHost()等完整 API 调用,Level 1 仅输出拓扑结构字符串,无法直接运行。
导出的.py文件本质是一个标准 Mininet Topo 类继承体,结构如下:
#!/usr/bin/python from mininet.topo import Topo from mininet.net import Mininet from mininet.cli import CLI from mininet.log import setLogLevel class MyTopo( Topo ): "Simple topology example." def __init__( self ): "Create custom topo." # Initialize topology Topo.__init__( self ) # Add hosts and switches h1 = self.addHost( 'h1', ip='10.0.0.1/24' ) h2 = self.addHost( 'h2', ip='10.0.0.2/24' ) s1 = self.addSwitch( 's1' ) # Add links self.addLink( h1, s1 ) self.addLink( h2, s1 ) topos = { 'mytopo': ( lambda: MyTopo() ) }这段代码不是“作业答案”,而是可版本管理、可 CI 测试、可嵌入自动化流水线的基础设施即代码(IaC)。你随时可以git diff查看拓扑变更,pytest测试pingAll()是否通过,甚至用ansible批量部署到多台测试机。
2.2 命令行速建拓扑:--topo参数背后的拓扑生成器原理
Mininet 内置了single、linear、reversed、torus、tree等拓扑生成器,它们不是预设图片,而是 Python 函数,在mininet/topo.py中定义。例如--topo linear,4对应的源码逻辑是:
# mininet/topo.py 中 LinearTopo 类片段 class LinearTopo( Topo ): "Linear topology of k switches, with n hosts per switch." def __init__( self, k=2, n=1, **kwargs ): super( LinearTopo, self ).__init__( **kwargs ) self.k = k self.n = n lastSwitch = None for i in range( k ): # Add switch switch = self.addSwitch( 's%s' % i ) # Add hosts to switch for j in range( n ): host = self.addHost( 'h%s' % ( i * n + j + 1 ) ) self.addLink( host, switch ) # Connect switches in line if lastSwitch: self.addLink( switch, lastSwitch ) lastSwitch = switch所以sudo mn --topo linear,4实际生成的是:4 个串联交换机(s0-s1-s2-s3),每个交换机挂 1 个主机(h1~h4),共 4 主机 4 交换机,链路数为 7(4 主机链 + 3 级联链)。
常用组合及生成效果:
| 命令 | 主机数 | 交换机数 | 拓扑形态 | 典型用途 |
|---|---|---|---|---|
sudo mn --topo single,3 | 3 | 1 | 星型(h1/h2/h3 全连 s1) | 测试广播域、ARP 行为 |
sudo mn --topo linear,4 | 4 | 4 | 链式(h1-s0-s1-s2-s3-h4) | 模拟广域网链路延迟 |
sudo mn --topo tree,depth=2,fanout=2 | 4 | 3 | 二叉树(s0→s1/s2,s1→h1/h2,s2→h3/h4) | 验证多级流表下发 |
注意:
--topo仅定义连接关系,不配置 IP、不启动控制器。若需指定控制器,必须加--controller remote,ip=127.0.0.1,port=6653;若需自定义主机 IP,必须配合--host参数(如--host=host,ip=10.0.0.1/24),但该参数对--topo的兼容性较差,强烈建议用 Python 脚本替代。
2.3 交互式 CLI:py命令不是玩具,是网络状态的实时探针
启动sudo mn后进入的mininet>提示符,本质是 Python 解释器封装层,所有py命令都直接调用 Mininet 对象的属性和方法。它不是“命令行模式”,而是网络对象的 REPL(Read-Eval-Print Loop)环境。
最常被低估的三个py操作:
动态添加设备并热链接
mininet> py 's2 = net.addSwitch("s2")' mininet> py 'h5 = net.addHost("h5", ip="10.0.0.5/24")' mininet> py 'net.addLink(h5, s2)' mininet> py 'net.start()' # 必须显式 start 才生效此操作绕过拓扑类定义,实现运行时网络重构——类似在 Kubernetes 中
kubectl apply -f动态扩缩容。获取节点进程 PID 并直接调试
mininet> py 'print(net.get("h1").pid)' mininet> py 'print(net.get("s1").pid)'返回的 PID 可直接用于
strace -p <PID>抓系统调用,或tcpdump -i h1-eth0抓包,无需退出 Mininet。执行主机原生命令并捕获输出
mininet> py 'print(net.get("h1").cmd("ping -c2 10.0.0.2"))' mininet> py 'print(net.get("h1").cmd("ip route show"))'cmd()方法返回字符串结果,可用于自动化断言(如assert "64 bytes" in net.get("h1").cmd("ping -c1 10.0.0.2"))。
这些能力让 CLI 成为故障定位第一现场:当pingall失败时,不用重启整个网络,只需py net.get("h1").cmd("ip addr")查 IP,py net.get("s1").cmd("ovs-ofctl dump-flows s1")查流表,py net.get("c0").cmd("netstat -tuln | grep 6653")查控制器监听状态——三步定位根因。
3. Python 脚本构建拓扑:从静态定义到带约束的生产级网络
Mininet 的 Python API 是其工程化落地的核心。miniedit导出的脚本只是起点,真正要支撑测试、CI、性能压测,必须掌握Topo类定制、addLink()带参链接、Host子类重载等进阶写法。本节聚焦四个高频需求:IP 地址精确控制、链路性能约束、CPU 限频模拟、多控制器接入。
3.1 主机 IP 与子网划分:为什么ip='10.0.0.1/24'必须带掩码?
Mininet 主机默认使用 DHCP 获取 IP(由mininet.util中的dhcpd启动),但 DHCP 在无控制器或控制器未下发 DHCP 规则时极易失败。生产环境必须禁用 DHCP,强制静态 IP。
正确写法:
h1 = self.addHost('h1', ip='10.0.0.1/24') # ✅ 掩码 /24 表示子网 10.0.0.0/24 h2 = self.addHost('h2', ip='10.0.0.2/24') s1 = self.addSwitch('s1') self.addLink(h1, s1) self.addLink(h2, s1)错误写法:
h1 = self.addHost('h1', ip='10.0.0.1') # ❌ 无掩码,ifconfig 显示 eth0: <NO-CARRIER>原因在于 Mininet 底层调用ip addr add命令,该命令要求 CIDR 格式。若省略掩码,系统无法确定子网范围,导致 ARP 请求发不出去(arping -I h1-eth0 10.0.0.2无响应)。
更严谨的做法是显式指定网关(虽非必需,但符合真实网络习惯):
h1 = self.addHost('h1', ip='10.0.0.1/24', defaultRoute='via 10.0.0.254')此时h1的路由表会增加default via 10.0.0.254 dev h1-eth0,便于后续对接外部网关设备。
3.2 链路性能约束:bw、delay、loss参数的真实物理意义与取值边界
addLink()的bw(带宽)、delay(延迟)、loss(丢包率)参数,直接映射 Linuxtc(traffic control)命令,是 Mininet 区别于纯仿真器的关键——它在内核层面注入真实网络行为。
参数含义与典型值:
| 参数 | 单位 | 说明 | 生产环境参考值 | Mininet 限制 |
|---|---|---|---|---|
bw | Mbit/s | 令牌桶速率上限 | 10M(园区网)、100M(数据中心)、1G(骨干) | 最小 0.1,最大约 10000(受宿主机网卡驱动限制) |
delay | ms/us/ns | 固定延迟(netem的delay) | 1ms(局域网)、20ms(跨省)、100ms(国际) | 支持ms/us/ns,但<1ms可能被内核四舍五入 |
loss | % | 随机丢包率(netem的loss random) | 0.01%(光纤)、1%(Wi-Fi)、5%(弱信号 4G) | 实测 >10% 时ping丢包率不稳定,建议 ≤5% |
示例:构建一条“高延迟低带宽”的模拟链路(如卫星链路):
self.addLink(h1, s1, bw=2, delay='300ms', loss=1)执行后,h1-eth0接口实际被注入tc qdisc add dev h1-eth0 root netem delay 300ms loss 1% rate 2mbit。
注意:
bw限速是单向的(从 h1 发往 s1),若需双向限速,必须为反向链路(s1→h1)单独设置bw。Mininet 不自动创建双向链路,addLink(h1,s1)仅创建 h1→s1 方向,s1→h1 由交换机内部转发,不受限。
3.3 CPU 限频与进程隔离:用cpu参数模拟资源受限终端
addHost()的cpu参数(如cpu=.5)并非设置 CPU 核心数,而是通过cgroups限制该主机进程的 CPU 使用率上限。其值为小数,表示可用 CPU 时间片比例(1.0 = 100%,0.1 = 10%)。
h1 = self.addHost('h1', cpu=.3) # h1 进程最多占用 30% 的单核 CPU h2 = self.addHost('h2', cpu=1.0) # h2 不限频此功能用于模拟 IoT 设备、嵌入式网关等低算力终端。当h1运行iperf3 -s服务端时,其吞吐量会被cgroups硬性截断,htop中可见h1进程 CPU% 恒定在 30% 左右。
验证方式:
mininet> py 'print(net.get("h1").cmd("cat /sys/fs/cgroup/cpu/h1/cpu.cfs_quota_us"))' # 输出 30000 mininet> py 'print(net.get("h1").cmd("cat /sys/fs/cgroup/cpu/h1/cpu.cfs_period_us"))' # 输出 100000cfs_quota_us / cfs_period_us = 0.3,即 30%。
提示:
cpu参数仅在 Linux 内核启用CONFIG_CGROUP_SCHED时生效(Ubuntu/Debian 默认开启)。若mininet启动时报cgroups not mounted,需手动挂载:sudo mkdir -p /sys/fs/cgroup/cpu && sudo mount -t cgroup -o cpu none /sys/fs/cgroup/cpu。
3.4 多控制器接入:--controller与addController()的分工
Mininet 支持单控制器(--controller remote)和多控制器(addController())模式。前者适用于测试单一控制器逻辑;后者用于验证控制器集群、主备切换、分片管理等高级场景。
多控制器脚本范式:
from mininet.node import RemoteController def createNet(): net = Mininet(topo=None, build=False) # 添加两个远程控制器 c0 = net.addController('c0', controller=RemoteController, ip='127.0.0.1', port=6653) c1 = net.addController('c1', controller=RemoteController, ip='127.0.0.1', port=6654) # 创建交换机,并指定主备控制器 s1 = net.addSwitch('s1', controllers=[c0, c1]) s2 = net.addSwitch('s2', controllers=[c0, c1]) # 添加主机 h1 = net.addHost('h1', ip='10.0.0.1/24') h2 = net.addHost('h2', ip='10.0.0.2/24') # 连接 net.addLink(h1, s1) net.addLink(h2, s2) net.addLink(s1, s2) net.build() net.start() # 启动 CLI CLI(net) net.stop()关键点:
controllers=[c0,c1]表示交换机支持双控制器连接,具体由控制器自身协议(如 OpenFlow 1.3 的OFPT_FEATURES_REPLY)协商主从;c0和c1必须是真实运行的控制器进程(如ryu-manager --ofp-tcp-listen-port 6653 app.ofctl_rest);- 此模式下
pingall成功,证明交换机已成功连接任一控制器并下发默认流表(table-miss到控制器)。
4. 避坑:Mininet 实验中最常翻车的五个边界问题
Mininet 看似简单,但新手常在看似无关的细节上卡住数小时。以下是我在某高校实验室带教、某公司 SDN 测试平台搭建中,被问得最多的五个“血泪问题”,按现象→原因→解决三段式整理,拒绝玄学,只讲可验证动作。
4.1 现象:miniedit启动黑屏或报TclError: no display name
原因:SSH 连接未启用 X11 转发,或本地 X Server(如 Windows 的 VcXsrv、macOS 的 XQuartz)未运行/未授权。Mininet GUI 依赖 Tkinter,而 Tkinter 需要 X11 显示服务。
解决:
- 本地安装 X Server 并启动(Windows 推荐 VcXsrv,勾选 “Disable access control”);
- SSH 连接时加
-X参数:ssh -X user@192.168.56.101; - 连接后执行
echo $DISPLAY,应输出localhost:10.0;若为空,执行export DISPLAY=localhost:10.0; - 再运行
sudo python3 /usr/local/bin/miniedit.py。
4.2 现象:pingall全部超时,但h1 ping h2却成功
原因:pingall默认使用 ICMP echo request,而某些控制器(如早期 Ryu)未下发 ICMP 处理流表,导致pingall的h1→h2包被丢弃;但h1 ping h2是主机间直连,走的是 Linux 内核协议栈,不经过 OpenFlow 流表。
解决:
- 进入 Mininet CLI,执行
py 'net.get("s1").cmd("ovs-ofctl dump-flows s1")',检查是否有icmp相关流表; - 若无,手动下发:
py 'net.get("s1").cmd("ovs-ofctl add-flow s1 priority=65535,icmp,actions=flood")'; - 更彻底方案:在控制器中加载
ryu.app.simple_switch_13(Ryu)或pox.forwarding.l2_learning(POX),它们默认处理 ICMP。
4.3 现象:sudo mn --topo linear,4启动后h1无法 ping 通h4,但相邻主机(h1↔h2)正常
原因:linear拓扑中,中间交换机(s1/s2/s3)默认无控制器连接,h1→h2→s1→s2→h3→h4路径中,s1/s2 无流表,数据包在 s1/s2 处被丢弃。--topo模式默认不启动控制器。
解决:
必须显式指定控制器:
sudo mn --topo linear,4 --controller remote,ip=127.0.0.1,port=6653且确保127.0.0.1:6653有控制器进程在监听(如ryu-manager --ofp-tcp-listen-port 6653 ryu.app.simple_switch_13)。
4.4 现象:Python 脚本中addLink(h1,s1,bw=10)生效,但h1 iperf3 -c 10.0.0.2测出带宽远超 10Mbit/s
原因:bw参数仅限制h1→s1方向,而iperf3客户端(h1)发送数据到服务端(h2)时,路径为h1→s1→s2→h2,其中s1→s2和s2→h2链路未设限,瓶颈不在h1→s1。
解决:
为整条路径所有链路设限:
self.addLink(h1, s1, bw=10) self.addLink(s1, s2, bw=10) # 关键!补上中间链路 self.addLink(s2, h2, bw=10)或使用tc手动限速:在脚本net.start()后插入
net.get('h1').cmd('tc qdisc add dev h1-eth0 root tbf rate 10mbit burst 32kbit latency 400ms')4.5 现象:miniedit导出的脚本运行时报AttributeError: 'NoneType' object has no attribute 'addLink'
原因:miniedit导出时未勾选Start CLI,导致导出脚本中net = Mininet(...)初始化时controller参数为None,net.build()失败,net对象未完全构建。
解决:
- 重新打开
miniedit,菜单栏Edit → Preferences→ 勾选Start CLI; - 重建拓扑并导出;
- 或手动修复导出脚本:在
net = Mininet(...)行中,将controller=None改为controller=RemoteController,并添加c0 = net.addController('c0', ip='127.0.0.1', port=6653)。
5. 性能验证与进阶技巧:用iperf3+ovs-ofctl+tcpdump三位一体验证网络行为
一份合格的 Mininet 实验报告,不能止于pingall通过。真正的工程价值在于:你能用标准工具链,量化验证你写的拓扑、设的参数、连的控制器,是否真的产生了预期效果。本节给出一套可直接复用的验证组合拳,覆盖带宽、延迟、流表、协议四维度。
5.1 带宽验证:iperf3客户端/服务端分离部署法
iperf3是验证bw参数是否生效的黄金标准。但必须注意:服务端(-s)和客户端(-c)必须部署在不同主机,且路径经过被限速链路。
标准验证流程:
# 启动 Mininet 网络(以 single,2 为例) sudo mn --topo single,2 --controller remote,ip=127.0.0.1,port=6653 # 在 Mininet CLI 中: mininet> h2 iperf3 -s & # h2 启动服务端,后台运行 mininet> h1 iperf3 -c 10.0.0.2 -t 10 -i 1 # h1 作为客户端,测 10 秒,每秒输出若addLink(h1,s1,bw=5)设置正确,输出应稳定在5.0 Mbits/sec左右(允许 ±0.2 浮动)。若远高于 5M,则检查链路是否遗漏限速(见 4.4);若远低于 5M,检查控制器是否阻塞 TCP 握手(ovs-ofctl dump-flows s1中是否有tcp,tcp_flags=0x02流表)。
提示:
iperf3默认使用 TCP,若要测 UDP 丢包率,加-u -b 5M参数(如h1 iperf3 -c 10.0.0.2 -u -b 5M -t 10),此时loss参数效果可被直接观测。
5.2 延迟验证:ping与mtr的互补使用
ping只能测往返时间(RTT),而addLink(delay='10ms')设置的是单向延迟。要验证单向延迟,需用mtr(My TraceRoute):
mininet> h1 mtr --report --interval 1 --count 5 10.0.0.2mtr输出中Loss%列应接近 0,Avg列应为20ms左右(10ms 单向 ×2)。若Avg显著大于 20ms(如 50ms),说明宿主机负载过高或netem延迟未生效,需检查tc qdisc show dev h1-eth0输出是否含delay 10ms。
5.3 流表验证:ovs-ofctl解析 OpenFlow 流表语义
ovs-ofctl dump-flows s1输出是十六进制流表,人类难读。用--names参数可解析为易懂字段:
mininet> py 'print(net.get("s1").cmd("ovs-ofctl dump-flows s1 --names"))'关键字段解读:
priority=65535:最高优先级,匹配所有包;icmp,dl_type=0x0800,nw_proto=1:匹配 ICMP 协议;actions=output:"s1-eth2":转发到端口s1-eth2(即连接 h2 的端口);idle_age=12:该流表项已空闲 12 秒,超时后自动删除(idle_timeout默认 60 秒)。
若发现actions=drop或actions=CONTROLLER,说明控制器未下发转发规则,需检查控制器日志。
5.4 协议验证:tcpdump抓包定位协议栈行为
当ping失败但ovs-ofctl显示流表正常时,问题可能出在协议栈。用tcpdump在主机接口抓包:
mininet> h1 tcpdump -i h1-eth0 icmp -c 4 -w /tmp/h1_icmp.pcap & mininet> h2 tcpdump -i h2-eth0 icmp -c 4 -w /tmp/h2_icmp.pcap & mininet> h1 ping -c 4 10.0.0.2 mininet> py 'print(net.get("h1").cmd("ls -lh /tmp/h1_icmp.pcap"))'将/tmp/h1_icmp.pcap下载到本地用 Wireshark 打开,可清晰看到:
- h1 是否发出
Echo Request; - h2 是否收到并回复
Echo Reply; - 若 h1 发出但 h2 未收到,说明链路层或交换机转发异常;
- 若 h2 收到但未回复,说明 h2 的 ICMP 响应被防火墙拦截(
sudo ufw disable临时关闭)。
5.5 终极技巧:用mn -c一键清理残留进程,避免“越试越错”
Mininet 异常退出后,常残留ovs-vswitchd、ovsdb-server、iperf3等进程,导致下次sudo mn启动失败(报port already in use或bridge exists)。手动ps aux | grep ovs杀进程极易遗漏。
我的固定动作:每次实验结束,必敲:
sudo mn -c该命令会:
- 清理所有 Mininet 创建的网络命名空间(
ip netns list为空); - 删除所有 OVS 网桥(
ovs-vsctl list-br为空); - 终止所有
ovs-*、iperf*、xterm进程; - 重置
tc队列(tc qdisc del dev ...)。
从那以后我每次做完 Mininet 实验,无论成功与否,都强制走一遍
sudo mn -c,再sudo mn --test pingall验证环境干净。这个习惯让我少踩 80% 的“环境污染”类坑。希望帮到你。
本文还有配套的精品资源,点击获取