简介这份资源面向计算机网络课程学习者与实验实践者围绕基于Python的网络拓扑实验展开重点解决传输机制实验中拓扑运行与文件收发功能的实现问题。包内共96个文件以C源码与头文件为主体28个.c、40个.h辅以14个Shell脚本、4个Python脚本及Makefile、数据文件、实验报告PDF、演示PPT与说明文档等压缩包约2.65MB结构上按实验阶段与代码版本分目录组织便于对照查阅。实验内容涵盖运行给定网络拓扑tcp_topo.py以及修改tcp_apps.c与tcp_stack.py实现文件收发配套报告与讲义可辅助理解协议栈设计思路。目前已有172人学习适合需要完成课程设计、理解TCP传输机制与网络拓扑搭建的读者参考可从中获取可运行的实验代码、分阶段实现思路与调试排错线索。1. 用 Python 把网络拓扑跑起来从邻接矩阵到可视化一套能复现的最小实验手里只有一台笔记本、一个 Python 环境能不能把「网络拓扑」这件事从抽象概念变成看得见、算得动的东西能。我做过很多次这类实验核心就三件事把拓扑抽象成图、把图存成矩阵或邻接表、再把结果画出来或者算指标。标题里的「基于 Python 实现网络拓扑的实验」本质就是用 Python 的图论工具链把节点和链路的关系建模、计算、可视化最后验证拓扑特性连通性、中心度、最短路径、鲁棒性。它适合两类人一类是刚学完 Python 基础、想找个能落地的练手项目的新手另一类是做运维、网络规划、甚至量化交易里研究关联网络的从业者需要快速验证一个拓扑假设。下面这套流程我在自己机器上反复跑过环境干净、依赖少、每一步都能单独验证。2. 拓扑建模先想清楚节点、边、有向还是无向2.1 网络拓扑在代码里到底长什么样网络拓扑在数学上就是一个图 G(V,E)V 是节点集合E 是边集合。落到 Python 里最常用的两种表示是邻接矩阵和邻接表。邻接矩阵是一个 N×N 的二维数组matrix[i][j]1表示 i 和 j 之间有链路邻接表则是每个节点维护一个邻居列表。选哪个取决于你的拓扑规模和后续要算什么节点数在几百以内、要做矩阵运算比如谱聚类、特征值分析用邻接矩阵节点上千、边稀疏用邻接表省内存。我一般先用邻接矩阵把逻辑跑通因为直观、好调试等规模上来了再换networkx的图对象。这里有个容易翻车的点无向图的邻接矩阵必须对称matrix[i][j]和matrix[j][i]要同时置 1。我见过有人只填了上三角结果算出来的度数是实际的一半排查了半天。有向图则不用对称但要注意入度和出度的区别。另外带权拓扑比如链路延迟、带宽就把 1 换成具体权重0 表示不通。下面这段代码用纯 Python 列表构造一个 6 节点的无向拓扑不依赖任何第三方库方便你先验证逻辑。# 6 节点无向拓扑的邻接矩阵构造 N 6 # 初始化全 0 矩阵 adj [[0] * N for _ in range(N)] # 定义链路(起点, 终点)无向图两个方向都要置 1 edges [(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)] for u, v in edges: adj[u][v] 1 adj[v][u] 1 # 无向图必须对称漏掉这行度数会算错 # 打印矩阵方便肉眼检查 for row in adj: print(row)逻辑说明adj是 N×N 的嵌套列表edges里每条链路都双向赋值保证对称。参数说明N是节点总数改拓扑时同步改edges即可如果是有向图删掉adj[v][u] 1那一行。跑完你应该看到一个对角线为 0、其余对称的 0/1 矩阵。这一步别急着上可视化先把矩阵打印出来核对比后面画图发现错了再回头找要省事得多。2.2 用 networkx 建图比手写矩阵省一半代码纯列表适合理解原理但真要做实验networkx是绕不开的。它把节点、边、属性都封装好了还能直接调最短路径、中心度、连通分量这些算法。安装就一行pip install networkx如果你还要画图再装matplotlib。我一般会同时装这两个画图那步用得上。下面把上面的拓扑用networkx重建并顺手算几个基础指标。import networkx as nx G nx.Graph() # 无向图有向图用 nx.DiGraph() edges [(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)] G.add_edges_from(edges) print(节点数:, G.number_of_nodes()) print(边数:, G.number_of_edges()) print(各节点度数:, dict(G.degree())) print(是否连通:, nx.is_connected(G)) print(平均最短路径长度:, nx.average_shortest_path_length(G))逻辑说明add_edges_from一次性把边列表灌进去节点会自动创建。G.degree()返回每个节点的度is_connected判断整个图是否连通average_shortest_path_length算平均跳数。参数说明nx.Graph()换成nx.DiGraph()就是有向图换成nx.MultiGraph()支持两点间多条链路。这几个指标是拓扑实验的「体检报告」连通性为 False 说明有孤立子图平均路径长度能反映拓扑的紧凑程度。跑通这步你就有了一个可计算、可扩展的拓扑对象后面所有实验都基于它。3. 从矩阵到图可视化与指标计算的完整链路3.1 用 matplotlib 把拓扑画出来节点位置怎么定拓扑画出来才直观但节点位置layout是新手最容易卡住的地方。networkx内置了好几种布局spring_layout模拟弹簧斥力适合一般拓扑circular_layout把节点排成圆适合看环形结构kamada_kawai_layout基于距离画出来的图比较规整。我一般先用spring_layout因为它对大多数拓扑都能给出可读的结果。下面这段代码把图存成 PNG注意matplotlib在无界面环境比如服务器要指定后端否则会报错。import matplotlib matplotlib.use(Agg) # 无界面环境必须加否则 plt.show() 会崩 import matplotlib.pyplot as plt import networkx as nx G nx.Graph() G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)]) pos nx.spring_layout(G, seed42) # seed 固定保证每次布局一致 nx.draw_networkx_nodes(G, pos, node_colorlightblue, node_size500) nx.draw_networkx_edges(G, pos, width1.5) nx.draw_networkx_labels(G, pos, font_size12) plt.axis(off) plt.savefig(topology.png, dpi150, bbox_inchestight) print(已保存 topology.png)逻辑说明spring_layout返回每个节点的 (x, y) 坐标字典seed固定后每次运行布局相同方便对比不同拓扑。draw_networkx_nodes/edges/labels分别画节点、边、标签。参数说明node_size控制节点大小dpi控制输出清晰度bbox_inchestight去掉多余白边。如果你在 Jupyter 里跑把matplotlib.use(Agg)去掉、savefig换成plt.show()即可。画完打开图片先看有没有孤立节点、有没有明显画错的边这一步能帮你抓出建模阶段的低级错误。3.2 中心度与最短路径拓扑实验里最常算的两个指标拓扑实验不只是画图更重要的是算指标。最常用的两个是中心度和最短路径。中心度衡量节点的重要性degree_centrality看连接数betweenness_centrality看节点在多少条最短路径上中介性closeness_centrality看节点到其他节点的平均距离。最短路径则用shortest_path和shortest_path_length。下面这段代码把三个中心度都算出来并找出节点 0 到 5 的最短路径。import networkx as nx G nx.Graph() G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)]) deg_c nx.degree_centrality(G) btw_c nx.betweenness_centrality(G) clo_c nx.closeness_centrality(G) for n in G.nodes(): print(f节点{n}: 度中心度{deg_c[n]:.3f}, f中介中心度{btw_c[n]:.3f}, 接近中心度{clo_c[n]:.3f}) path nx.shortest_path(G, source0, target5) length nx.shortest_path_length(G, source0, target5) print(0 到 5 的最短路径:, path, 长度:, length)逻辑说明三个中心度函数都返回字典键是节点、值是分数。shortest_path返回节点序列shortest_path_length返回跳数。参数说明betweenness_centrality可以加weight参数支持带权图shortest_path默认按跳数算带权图要传weightweight。跑完你会发现节点 3 的中介中心度最高因为它连接了左右两个子图是拓扑里的「咽喉」。这个结论在真实网络里很有用——咽喉节点一旦故障整个网络可能分裂。我一般会把中心度排序后取 Top 3作为重点监控对象。3.3 把邻接矩阵和 networkx 互相转换实际项目里经常需要在两种表示之间切换比如从数据库读出来的是矩阵但算法要用networkx或者用networkx算完要导出成矩阵给别的模块。转换本身不难但有几个细节容易出错。下面这段代码演示双向转换并验证转换前后一致。import networkx as nx import numpy as np # networkx - 邻接矩阵 G nx.Graph() G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)]) mat nx.to_numpy_array(G) print(矩阵形状:, mat.shape) print(mat) # 邻接矩阵 - networkx G2 nx.from_numpy_array(mat) print(还原后边数:, G2.number_of_edges()) print(与原图是否同构:, nx.is_isomorphic(G, G2))逻辑说明to_numpy_array把图转成 numpy 矩阵from_numpy_array反向还原。is_isomorphic验证两个图结构是否一致。参数说明to_numpy_array默认按节点标签排序如果节点标签不是 0 到 N-1 的整数要先G nx.convert_node_labels_to_integers(G)再转否则矩阵行列顺序会对不上。这个坑我踩过——节点标签是字符串时直接转矩阵看着没问题但和原始节点对应关系全乱了。转换完一定要用is_isomorphic验一下这是最省事的后悔药。4. 避坑与排查拓扑实验里最容易翻车的 5 个地方4.1 邻接矩阵不对称导致度数算错现象算出来的节点度数总是实际的一半或者连通性判断为 False。原因无向图只填了上三角adj[i][j]1但adj[j][i]还是 0。解决构造时强制双向赋值或者构造完加一句adj adj adj.Tnumpy 矩阵再检查对角线是否为 0。我现在的习惯是写完矩阵先打印出来肉眼扫一遍对称性比后面算法报错再回头找快得多。4.2 matplotlib 在服务器上报 no display 错误现象本地跑得好好的画图代码放到服务器或 Docker 里就报no display name and no $DISPLAY environment variable。原因matplotlib默认用交互式后端无界面环境没有显示设备。解决在import matplotlib.pyplot之前加matplotlib.use(Agg)强制用非交互后端。注意这行必须在pyplot导入之前顺序反了不生效。如果还要在 Jupyter 里显示就只在服务器脚本里加别写进公共模块。4.3 节点标签不是整数时矩阵行列错位现象用to_numpy_array转出来的矩阵和原始节点对应不上算出来的中心度张冠李戴。原因to_numpy_array默认按节点标签排序字符串标签排序结果和插入顺序不一致。解决转换前先G nx.convert_node_labels_to_integers(G, first_label0)把标签统一成 0 到 N-1转换后再用映射表还原。或者直接用nodelist参数指定顺序nx.to_numpy_array(G, nodelistsorted(G.nodes()))。4.4 spring_layout 每次画出来不一样现象同一个拓扑每次运行画出来的节点位置都不同没法做对比图。原因spring_layout初始位置是随机的。解决加seed参数固定随机种子比如nx.spring_layout(G, seed42)。这样每次布局一致方便把不同拓扑的画图结果并排对比。如果要做论文级别的图还可以先算好pos存成 JSON画图时直接读彻底消除随机性。4.5 大规模拓扑用纯 Python 列表内存爆炸现象节点数上千后邻接矩阵占用内存飙升程序卡死。原因N×N 矩阵的内存是 O(N²)10000 个节点就是 1 亿个元素。解决节点数超过 1000 且边稀疏时改用邻接表或直接上networkx的图对象内部用字典存储内存是 O(NE)。如果必须用矩阵考虑scipy.sparse的稀疏矩阵只存非零元素。我一般以 500 个节点为分界线超过就换稀疏表示。5. 进阶技巧用拓扑鲁棒性实验验证你的网络设计前面都是基础操作真正能体现拓扑实验价值的是鲁棒性分析——模拟节点或边失效看网络会不会分裂。这个技巧在运维容量规划、网络架构评审里特别实用。做法很简单依次移除每个节点重新算连通分量数量和最大连通子图的大小下降越快说明拓扑越脆弱。下面这段代码对 6 节点拓扑做节点移除实验输出每个节点失效后的最大连通子图占比。import networkx as nx G nx.Graph() G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)]) N G.number_of_nodes() print(节点移除鲁棒性分析) for node in list(G.nodes()): H G.copy() H.remove_node(node) if H.number_of_nodes() 0: print(f移除节点{node}: 网络完全消失) continue largest max(nx.connected_components(H), keylen) ratio len(largest) / N print(f移除节点{node}: 最大连通子图占比{ratio:.2f}, f连通分量数{nx.number_connected_components(H)})逻辑说明每次复制原图、移除一个节点然后算最大连通子图占原节点总数的比例。占比越低说明该节点越关键。参数说明connected_components返回连通分量的节点集合max(..., keylen)取最大的那个。跑完你会看到移除节点 3 后占比明显下降因为它连接左右两个子图。这个实验可以扩展到边移除、随机失效、按中心度排序失效等多种策略。我一般会把结果画成折线图横轴是移除节点数纵轴是最大连通子图占比曲线下降越陡说明拓扑越脆弱。这个技巧的价值在于它把「拓扑设计好不好」从主观判断变成了可量化对比。你可以设计两套拓扑方案分别跑鲁棒性实验看哪套在关键节点失效后仍能保持连通。我自己的习惯是任何拓扑方案上线前都跑一遍这个实验把最脆弱的三个节点标出来作为冗余设计的重点。这套流程不依赖任何外部服务纯 Python 加networkx就能跑适合反复迭代。希望帮到你。本文还有配套的精品资源点击获取