
简介这份资源是黑龙江大学计算机网络课程的实验报告与配套源码合集面向正在学习计算机网络、需要动手实践协议原理的高校学生与自学者。内容围绕Winsock编程、HTTP协议简单实现、停止等待ARQ算法、RIP路由协议以及ping、traceroute等常用网络命令展开用Python将抽象协议落地为可运行代码适合课程实验参考与期末复习。压缩包共24个文件以11个py源码、5个doc实验报告、4个html页面为主另含少量pyc、txt与png整体约341KB体积轻便便于按实验模块对照阅读。目前已有1081人学习下载。源码注释清晰、格式规范报告与代码相互印证读者可借此理解TCP/IP通信流程、错误检测与重传机制、距离矢量路由更新过程并积累Python网络编程的调试与排错经验是教学与自学兼顾的实践材料。1. 黑龙江大学计算机网络实验报告源码从抓包到RIP一套能跑通的Python实现如果你正在搜“黑龙江大学计算机网络实验报告源码Python实现”大概率是两种情况要么课设临近截止需要一份能跑通、能讲清原理的参考要么你不想只交一份截图拼凑的报告而是想真正把Socket、抓包、路由协议这些实验用Python从头实现一遍。这个标题背后对应的不是一份“文档”而是一套可复现的实验链路用Python写客户端/服务端、用抓包工具验证协议交互、用代码模拟RIP路由收敛最后把过程整理成实验报告。它适合计算机网络课程的本科生、需要快速补齐实验环节的自学者以及想用Python把协议细节“跑出来”的开发者。接下来我按实际做实验的顺序把每个环节的代码、参数和踩坑点拆开讲。2. 实验环境与Python网络编程底座先把Socket和抓包跑通2.1 为什么实验代码优先选Python而不是C黑龙江大学计算机网络实验通常覆盖应用层、传输层和网络层三个层次。应用层实验常见的是HTTP客户端/服务端、FTP简易实现传输层是UDP/TCP的可靠传输模拟网络层则是RIP路由协议的模拟。用C写当然更“底层”但实验报告的核心是验证协议行为不是比拼内存管理。Python的socket模块把系统调用封装得足够薄同时struct、threading、select这些库能让你在几十行内把并发和报文解析写清楚。我一般会建议学生用Python 3.8以上版本原因是asyncio在3.7之后稳定而实验里如果需要模拟多个路由节点并发发送更新报文异步比多线程更好控制。安装直接用官网安装包勾选“Add Python to PATH”然后在命令行验证python --version pip install scapyscapy是抓包和构造报文的关键库RIP实验里构造UDP报文、解析路由条目都靠它。如果安装scapy时报缺少libpcapWindows下需要先装NpcapLinux下装libpcap-dev。这一步翻车的同学不少现象是ImportError: cannot import name conf原因就是底层抓包库没装。2.2 用Socket写一个最小TCP回声服务端和客户端实验报告里第一个实验通常是“TCP套接字编程”。不要一上来就写多线程文件传输先把回声echo跑通确认端口、编码、连接关闭这三个点没问题。服务端代码import socket # 创建TCP套接字AF_INET表示IPv4SOCK_STREAM表示TCP server socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口复用避免重启时提示Address already in use server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((127.0.0.1, 8888)) # 绑定本地回环地址和端口 server.listen(5) # 最多允许5个等待连接 print(服务端已启动监听8888端口) while True: conn, addr server.accept() # 阻塞等待客户端连接 print(f收到来自{addr}的连接) while True: data conn.recv(1024) # 每次最多收1024字节 if not data: # 客户端关闭连接时recv返回空 break conn.sendall(data) # 原样发回 conn.close()客户端代码import socket client socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((127.0.0.1, 8888)) msg 黑龙江大学计算机网络实验 client.sendall(msg.encode(utf-8)) # 发送时必须编码为字节 resp client.recv(1024) print(收到回声:, resp.decode(utf-8)) client.close()逻辑说明bind的地址如果是0.0.0.0表示监听所有网卡实验里用127.0.0.1更安全。recv(1024)的1024是缓冲区大小如果发送的数据超过这个值需要循环接收否则会截断。参数上listen(5)的5是半连接队列长度实验环境够用但如果你要模拟高并发这个值要调大。注意sendall和send的区别是sendall会一直发送直到所有数据发完实验报告里如果写send遇到大数据包可能只发一部分导致对端解析错位。2.3 用Scapy抓包验证三次握手写完Socket代码实验报告需要“证据”。用Scapy抓一次本地回环的TCP握手把包序展示出来。from scapy.all import sniff, TCP # 只抓TCP且端口为8888的包count10表示抓10个就停 packets sniff(filtertcp port 8888, count10, ifacelo) for pkt in packets: if TCP in pkt: flags pkt[TCP].flags print(f{pkt[IP].src}:{pkt[TCP].sport} - {pkt[IP].dst}:{pkt[TCP].dport} flags{flags})在Linux下ifacelo是回环网卡Windows下回环抓包需要装Npcap并选Npcap Loopback Adapter。抓到的flags里S是SYNSA是SYNACKA是ACK正好对应三次握手。实验报告里把这三行输出贴上去比截图更有说服力。参数说明filter用的是BPF语法tcp port 8888表示只抓TCP且端口8888的包。count设太小可能抓不全握手设10比较稳。如果抓不到包先检查iface名字用get_if_list()列出所有网卡。3. RIP路由协议模拟用Python把距离向量算法跑出来3.1 RIP的核心不是协议报文是距离向量更新逻辑热搜词里“RIP”“rip协议”出现频率很高说明很多人卡在路由实验。RIPRouting Information Protocol的本质是距离向量算法每个路由器维护一张到所有目的网络的距离表距离用跳数表示最大15跳16跳表示不可达。每30秒向邻居发送自己的整张路由表邻居收到后按“下一跳是我吗”“距离是否更短”来更新。用Python模拟时不需要真的发UDP报文到224.0.0.9而是用字典表示路由表用循环模拟周期性更新。这样实验报告既能展示算法逻辑又不受网络环境限制。我一般会定义一个Router类每个实例有name和tabletable的每一项是(目的网络, 距离, 下一跳)。3.2 用字典和循环实现距离向量更新先定义初始拓扑R1连R2R2连R3R1直连网络10.0.0.0/8R2直连20.0.0.0/8R3直连30.0.0.0/8。初始时每个路由器只知道直连网络距离为1或0看约定实验里常用1。class Router: def __init__(self, name): self.name name self.table {} # 格式: {目的网络: (距离, 下一跳)} def add_direct(self, network): # 直连网络距离设为1下一跳为自己 self.table[network] (1, self.name) def send_table(self): # 返回可发送的路由表副本 return self.table.copy() def receive_table(self, neighbor_name, neighbor_table): updated False for net, (dist, next_hop) in neighbor_table.items(): new_dist dist 1 # 经过邻居后跳数加1 if new_dist 15: continue # 超过15跳视为不可达忽略 if net not in self.table: # 新网络直接学习 self.table[net] (new_dist, neighbor_name) updated True else: old_dist, old_hop self.table[net] if old_hop neighbor_name: # 下一跳就是该邻居无条件更新应对拓扑变化 if new_dist ! old_dist: self.table[net] (new_dist, neighbor_name) updated True elif new_dist old_dist: # 发现更短路径更新 self.table[net] (new_dist, neighbor_name) updated True return updated逻辑说明receive_table里有两个关键判断。第一如果下一跳就是发来表的邻居说明这条路由原本就是跟它学的现在它说距离变了必须无条件更新否则拓扑变化后旧路由永远删不掉。第二如果下一跳不是该邻居只有新距离更短才更新。这两个判断是RIP避免环路的核心实验报告里一定要写清楚。参数上15跳是RIP的硬限制new_dist 15就丢弃。实际实验里拓扑小跳数不会超但代码里保留这个判断能体现对协议的理解。3.3 模拟收敛过程并打印每轮路由表把三个路由器连起来跑5轮更新观察路由表变化。r1 Router(R1) r2 Router(R2) r3 Router(R3) r1.add_direct(10.0.0.0/8) r2.add_direct(20.0.0.0/8) r3.add_direct(30.0.0.0/8) # 邻居关系R1-R2, R2-R3 neighbors { R1: [R2], R2: [R1, R3], R3: [R2] } routers {R1: r1, R2: r2, R3: r3} for round_num in range(1, 6): print(f--- 第{round_num}轮 ---) # 先收集所有要发送的表避免边发边改 sent {name: r.send_table() for name, r in routers.items()} for name, r in routers.items(): for nb in neighbors[name]: r.receive_table(nb, sent[nb]) for name, r in routers.items(): print(f{name}: {r.table})运行后你会看到第1轮R1学到20.0.0.0/8距离2R2学到10.0.0.0/8和30.0.0.0/8距离2第2轮R1和R3学到对方网络距离3。到第3轮基本收敛。实验报告里把每轮输出贴出来再画一张收敛前后的路由表对比比只写“算法收敛”有说服力。注意sent字典必须在更新前统一收集如果一边遍历一边调用send_table先更新的路由器会影响后更新的导致结果不可复现。这是模拟类实验最常见的翻车点。4. 实验报告撰写与源码组织让代码和结论对得上4.1 报告结构按“目的-环境-步骤-结果-分析”五段走黑龙江大学计算机网络实验报告通常有固定模板但模板里的“实验步骤”很多人写成流水账。我建议按五段组织实验目的一句话说清验证哪个协议、实验环境Python版本、Scapy版本、操作系统、实验步骤贴关键代码和命令、实验结果抓包输出、路由表收敛过程、结果分析为什么三次握手是三次、RIP为什么最大15跳。源码组织上不要把所有代码塞进一个.py文件。按实验分目录exp1_socket/、exp2_rip/每个目录下放server.py、client.py、rip_sim.py再加一个README.md写运行顺序。这样报告里引用代码时可以说“见exp2_rip/rip_sim.py第23行”老师一看就知道你不是抄的。4.2 用表格对比RIP和静态路由的差异实验报告里如果只写RIP代码容易显得单薄。加一张对比表说明为什么动态路由在拓扑变化时更优。对比项静态路由RIP动态路由配置方式手动逐条添加自动学习拓扑变化需人工修改自动收敛开销无协议报文每30秒广播整表适用规模小型稳定网络小型动态网络最大跳数无限制15跳表格放在“结果分析”里配合你的模拟输出说明R1到30.0.0.0/8的路由是经过3轮学习得到的距离为3下一跳为R2。这样报告的技术密度就上来了。4.3 源码里必须有的注释和异常处理实验源码和工程源码的区别在于实验源码要让人看懂“这一步在验证什么”。所以每个函数上方写清楚协议依据比如receive_table上方写“依据RFC 1058距离向量算法在收到邻居更新时执行”。异常处理也要有比如Socket实验里connect可能抛ConnectionRefusedError要捕获并打印“服务端未启动”而不是让程序直接崩掉。报告里把异常处理代码也贴出来说明你考虑了边界情况。try: client.connect((127.0.0.1, 8888)) except ConnectionRefusedError: print(连接失败请先启动服务端) exit(1)这段代码虽短但实验报告里加上去老师会认为你真正跑过而不是只复制了正常流程。5. 避坑与排查RIP模拟和Socket实验里最容易翻车的5个点5.1 现象RIP模拟跑完路由表里出现16跳的条目原因receive_table里没有对new_dist 15做过滤或者过滤条件写成了 15。RIP规定16表示不可达15是最大有效跳数。如果邻居发来的距离是15加1后是16应该丢弃而不是写入表。解决在计算new_dist dist 1之后立即判断if new_dist 15: continue。注意是不是因为距离15本身是合法的。5.2 现象Socket实验在Windows上跑客户端连不上服务端原因Windows防火墙默认拦截入站连接或者服务端bind的地址写成了127.0.0.1而客户端用了本机IP。如果服务端只绑回环外部网卡来的连接会被拒绝。解决实验里统一用127.0.0.1或者服务端绑0.0.0.0并在防火墙里放行8888端口。报告里写清楚你用的是哪种避免老师复现时环境不一致。5.3 现象Scapy抓包抓不到任何包sniff直接超时原因iface参数写错或者没有用管理员/root权限。Windows下抓回环需要Npcap的Loopback适配器Linux下抓lo需要root。解决先用get_if_list()打印所有网卡名确认iface值。Linux下命令前加sudoWindows下用管理员权限打开终端。如果还是不行把filter去掉抓所有包看有没有输出逐步缩小范围。5.4 现象RIP模拟结果每轮都在变不收敛原因更新顺序有问题。如果遍历路由器时边更新边发送先更新的路由器会把新表发给后面的导致同一轮内信息传播了多次收敛轮数不可预测。解决每轮开始时用字典推导式统一快照所有路由表sent {name: r.send_table() for name, r in routers.items()}然后再遍历更新。这样每轮只传播一跳收敛过程清晰可复现。5.5 现象实验报告里的代码和运行结果对不上原因代码改过但报告没同步或者报告里的输出是手动编的。老师如果照着代码跑结果不一致整份报告的可信度就没了。解决报告里的每个输出都从终端直接复制不要手打。代码用git管理每次改完重新跑一遍把输出粘贴到报告里。如果时间紧至少保证核心实验Socket回声、RIP收敛的代码和输出一致。6. 进阶技巧把RIP模拟改成可视化收敛动画如果你想让实验报告从“合格”变成“有亮点”可以在RIP模拟基础上加一个简单的可视化。不需要复杂的GUI用matplotlib的animation就能画出每个路由器路由表的变化。核心思路是每轮更新后把每个路由器的table转成矩阵用热力图显示距离。import matplotlib.pyplot as plt import matplotlib.animation as animation import numpy as np # 假设有3个路由器3个网络 networks [10.0.0.0/8, 20.0.0.0/8, 30.0.0.0/8] routers [R1, R2, R3] def table_to_matrix(router_list): mat np.full((len(router_list), len(networks)), 16) # 默认16不可达 for i, r in enumerate(router_list): for j, net in enumerate(networks): if net in r.table: mat[i][j] r.table[net][0] return mat fig, ax plt.subplots() im ax.imshow(table_to_matrix([r1, r2, r3]), cmapYlOrRd, vmin0, vmax16) ax.set_xticks(range(len(networks))) ax.set_xticklabels(networks, rotation45) ax.set_yticks(range(len(routers))) ax.set_yticklabels(routers) def update(frame): # 执行一轮RIP更新 sent {name: r.send_table() for name, r in routers.items()} for name, r in routers.items(): for nb in neighbors[name]: r.receive_table(nb, sent[nb]) im.set_array(table_to_matrix([r1, r2, r3])) return [im] ani animation.FuncAnimation(fig, update, frames5, interval1000, blitTrue) plt.show()这段代码的逻辑是每帧执行一轮RIP更新然后用热力图刷新。颜色越深表示距离越大收敛后颜色稳定。参数上vmin0, vmax16固定色标范围避免每帧自动缩放导致视觉误导。frames5表示跑5轮interval1000是每帧间隔1秒。实验报告里可以把收敛过程的截图按轮次排列旁边标注“第1轮R1只知道直连网络第3轮全网收敛”。这种可视化比纯文字描述更能体现你对协议动态过程的理解。我自己的习惯是每次做协议模拟实验都至少加一个可视化输出因为调试时一眼就能看出哪一轮没更新比打印字典快得多。希望帮到你。本文还有配套的精品资源点击获取