ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

黑龙江大学计算机网络实验:Python实现抓包、Socket与RIP协议

黑龙江大学计算机网络实验:Python实现抓包、Socket与RIP协议 简介这份资源是黑龙江大学计算机网络课程的实验报告与配套源码合集面向正在学习计算机网络、需要动手实践协议原理的高校学生与自学者。内容围绕Winsock编程、HTTP协议简单实现、停止等待ARQ算法、RIP路由协议以及ping、traceroute等常用网络命令展开用Python将抽象协议落地为可运行代码帮助读者在编程中理解网络通信机制。压缩包共24个文件约341KB包含11个py源码、5个doc实验报告、4个html页面及少量pyc、txt、png辅助文件源码注释清晰、报告结构完整便于对照阅读与复现实验。目前已有1081人学习下载。读者可借此获得一套可直接运行的实验代码、规范的实验报告模板与协议实现思路适合作为课程作业参考、期末复习或网络编程入门练手材料。1. 黑龙江大学计算机网络实验报告源码从抓包到RIP一套能跑通的Python实现很多同学做计算机网络实验时卡点从来不是不会写代码而是不知道实验到底要验证什么。黑龙江大学这套实验报告加 Python 源码的组合核心价值在于把谢希仁《计算机网络》教材里那些抽象的分层概念落成了能抓包、能构造报文、能跑路由协议的具体程序。它覆盖的典型实验包括 Socket 编程、ARP 与 ICMP 报文分析、TCP 三次握手抓包以及 RIP 距离向量路由的模拟实现。适合两类人一是正在跟计算机网络实验课、需要交报告又要理解原理的本科生二是想用 Python 把网络协议从背概念变成看得见的自学者。下面我按先讲清原理、再给可复现代码、最后说坑的顺序把这套东西拆开讲透。2. 实验环境与 Python 网络编程底座为什么选 Python 而不是 C2.1 选型理由Python 在协议实验里的三个真实优势计算机网络实验的传统做法是用 C 写 Socket或者用 Wireshark 纯抓包分析。C 的麻烦在于内存管理和字节序转换一个htons忘了写抓出来的报文端口号就是乱的。Python 的socket和struct模块把这两件事都简化了struct.pack(!HH, src_port, dst_port)里的!直接声明网络字节序不用手动调htonl。第二个优势是迭代速度。写一个 ICMP 探测程序C 要编译、链接、处理权限Python 改一行直接跑。第三个优势是生态scapy库能构造任意协议层报文socket标准库能覆盖 90% 的实验需求不需要额外装重型依赖。常见做法是基础 Socket 实验用标准库socket报文构造和分析实验用scapy抓包验证用 Wireshark 或tcpdump配合。我一般会建议先跑通标准库版本理解bind、listen、accept的调用顺序再上scapy否则容易变成只会调库不懂协议。2.2 环境搭建三步把实验底座配好第一步确认 Python 版本和权限。网络实验涉及原始套接字raw socketLinux 下需要 root 或CAP_NET_RAW能力Windows 下需要管理员权限。# 查看 Python 版本建议 3.8 以上 python3 --version # 安装实验常用库 pip install scapy # Linux 下给 Python 解释器加原始套接字能力避免每次 sudo sudo setcap cap_net_raweip $(readlink -f $(which python3))第二步验证scapy能正常发包。这一步是很多人的第一个翻车点装完scapy直接import没问题但一发包就报权限错误。# check_env.py from scapy.all import IP, ICMP, sr1 # 发一个 ICMP 探测包到本地网关超时 2 秒 pkt IP(dst192.168.1.1) / ICMP() resp sr1(pkt, timeout2, verbose0) if resp: print(f收到回复: {resp.summary()}) else: print(无回复检查权限或目标地址)这段代码的逻辑是构造一个 IP 层目标为网关、上层为 ICMP 的包sr1表示发一个收一个。参数timeout2控制等待时间verbose0关掉冗余输出。如果报PermissionError回到第一步检查setcap或改用sudo。第三步配置 Wireshark 抓包过滤。实验报告里通常要求附抓包截图过滤表达式写不对截出来的图全是无关流量。常用过滤icmp、tcp.port 8080、arp、rip。提示Windows 下scapy依赖 Npcap安装时勾选WinPcap 兼容模式否则sr1会静默失败不报错但收不到包这个坑很隐蔽。3. Socket 编程实验TCP 回声服务器与客户端的最小可跑实现3.1 TCP 三次握手在代码里到底对应哪几行教材讲三次握手是 SYN、SYN-ACK、ACK 三个包。落到代码里服务端的listen()之后、accept()返回之前内核已经替你完成了握手。很多实验报告要求指出握手的代码位置答案就是connect()触发 SYNaccept()返回时握手已完成。理解这一点才不会在代码里找根本不存在的发送 SYN语句。服务端最小实现# tcp_server.py import socket HOST, PORT 0.0.0.0, 9000 # AF_INET 表示 IPv4SOCK_STREAM 表示 TCP with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # 允许端口复用避免 TIME_WAIT 导致重启报错 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST, PORT)) s.listen(5) # 半连接队列长度 print(f监听 {PORT} ...) conn, addr s.accept() # 阻塞直到握手完成 with conn: print(f连接来自 {addr}) while True: data conn.recv(1024) if not data: break conn.sendall(data) # 回声原样发回客户端# tcp_client.py import socket with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((127.0.0.1, 9000)) # 触发三次握手 s.sendall(bhello rip experiment) data s.recv(1024) print(f收到回声: {data.decode()})参数说明SO_REUSEADDR解决的是服务端重启时Address already in use的问题原因是上次连接的 TIME_WAIT 状态还占着端口。listen(5)的 5 是 backlog不是最大连接数别在报告里写错。recv(1024)的 1024 是单次读取上限不代表消息边界TCP 是字节流粘包问题要靠应用层协议解决。3.2 用 Wireshark 验证握手和挥手跑起上面两个程序后在 Wireshark 里过滤tcp.port 9000能看到完整的 SYN、SYN-ACK、ACK以及关闭时的 FIN、ACK、FIN、ACK。实验报告里要求标注序列号和确认号的变化规律这里给一个对照表阶段标志位序列号关系说明第一次握手SYNseqx客户端随机初始序列号第二次握手SYNACKseqy, ackx1服务端确认并给出自己的序列号第三次握手ACKseqx1, acky1客户端确认关闭第一次FINACKseqm主动关闭方发起关闭第二次ACKackm1被动方确认抓包时如果只看到 SYN 没有 SYN-ACK先检查服务端是否真的在listen再检查防火墙。Linux 下iptables -L看有没有 DROP 规则这是实验室环境里最常见的代码没错但连不上的原因。3.3 UDP 版本和 TCP 的差异点在哪UDP 实验代码更短因为没有握手和连接状态。服务端recvfrom直接收客户端sendto直接发。实验报告里常问UDP 为什么不可靠代码层面的证据就是sendto返回成功只代表数据交给了内核不代表对方收到。要验证丢包得在应用层加序号和确认这正好是 RIP 实验里要处理的问题。4. RIP 距离向量路由模拟从报文格式到收敛过程4.1 RIP 报文长什么样Python 怎么构造RIP 是应用层协议跑在 UDP 520 端口上。一个 RIP 报文包含命令请求/响应、版本号以及最多 25 个路由表项每项 20 字节地址族标识、路由标记、IP 地址、子网掩码、下一跳、跳数。跳数 16 表示不可达这是 RIP 防止路由环路的硬性上限。用scapy构造一个 RIP 响应报文# rip_packet.py from scapy.all import IP, UDP, RIP, RIPEntry, send # 构造一条路由表项目标 10.0.0.0/8下一跳 192.168.1.2跳数 2 entry RIPEntry(addr10.0.0.0, mask255.0.0.0, nexthop192.168.1.2, metric2) # 命令 2 表示响应版本 2 rip RIP(cmd2, version2, entries[entry]) pkt IP(dst224.0.0.9) / UDP(sport520, dport520) / rip send(pkt, verbose0) print(RIP 响应已发送)逻辑说明RIPEntry的metric就是跳数nexthop为0.0.0.0时表示由发送方自己作为下一跳。224.0.0.9是 RIP 组播地址版本 2 才支持组播版本 1 用广播。参数上cmd1是请求cmd2是响应实验里模拟路由更新发的是响应。4.2 距离向量算法的手写实现光会构造报文不够实验报告的核心是算法。距离向量算法的规则收到邻居的路由表后对每条路由把跳数加 1如果比自己的表里记录的更优跳数更小就更新如果下一跳就是该邻居无条件更新因为邻居的路径变了。# dv_algorithm.py def update_table(my_table, neighbor_table, neighbor_ip): my_table: {dest: (next_hop, metric)} neighbor_table: {dest: metric} updated False for dest, metric in neighbor_table.items(): new_metric min(metric 1, 16) # 超过 16 视为不可达 if dest not in my_table: my_table[dest] (neighbor_ip, new_metric) updated True else: old_hop, old_metric my_table[dest] # 来自同一邻居无条件更新否则只在更优时更新 if old_hop neighbor_ip or new_metric old_metric: if new_metric ! old_metric: my_table[dest] (neighbor_ip, new_metric) updated True return updated参数说明metric 1是距离向量的核心每经过一个路由器跳数加一。min(..., 16)是 RIP 的不可达约定防止计数到无穷。old_hop neighbor_ip这个判断是解决路由失效的关键如果邻居告诉你某条路变远了你得信它否则会一直用旧路径。4.3 用三个节点验证收敛过程搭一个三节点拓扑A 连 BB 连 C。初始时 A 只知道直连网段B 知道两个直连C 知道一个。跑几轮update_table观察 A 的路由表如何学到 C 的网段。轮次A 的路由表说明初始直连网段metric0只有自己第 1 轮学到 B 的直连metric1从 B 的更新里学第 2 轮学到 C 的网段metric2B 从 C 学来再传给 A稳定不再变化收敛完成实验报告里要求画收敛时序图用这个表就能说明。注意坏消息传得慢如果 C 的网段断了A 要等好几轮才能把 metric 涨到 16这就是 RIP 的收敛慢问题也是它被 OSPF 取代的原因之一。5. 实验报告避坑与排查五个真实踩过的坑5.1 抓包抓不到任何包现象Wireshark 选了网卡过滤也写了但一个包都没有。原因通常是选错了网卡或者流量走的是回环接口。本机测试127.0.0.1的流量不走物理网卡要在 Wireshark 里选Loopback或Adapter for loopback traffic。解决先ipconfig/ifconfig确认目标地址对应的接口再选对应网卡。5.2 scapy 发包含糊成功但收不到回复现象send()不报错sr1()返回None。原因有三权限不足导致包没真正发出、目标地址不可达、防火墙拦截了回复。解决先用ping确认目标可达再用sudo或setcap提权最后检查iptables。Windows 下重点查 Npcap 是否装对。5.3 RIP 模拟里路由表出现计数到无穷现象某条路由的 metric 一直涨停不下来。原因是没实现水平分割或毒性逆转。解决在update_table里加一条规则从某邻居学来的路由不再发回给该邻居水平分割或者发回时把 metric 设为 16毒性逆转。这是实验报告里区分能跑和跑对的关键点。5.4 Socket 服务端重启报端口占用现象OSError: [Errno 98] Address already in use。原因是上次连接的 TIME_WAIT 状态还没释放。解决加SO_REUSEADDR或者在测试时换端口。生产环境还要考虑SO_REUSEPORT但实验里SO_REUSEADDR够了。5.5 字节序搞反导致端口号乱码现象抓包看到源端口是 20480 而不是 80。原因是把主机字节序直接填进了报文。解决用struct.pack(!H, port)或scapy的字段自动处理!就是网络字节序。手写报文时所有多字节字段都要过一遍字节序转换。6. 进阶技巧把实验报告写成能复用的协议分析工具实验做完报告交完代码别扔。我习惯把 Socket 和 scapy 的代码整理成一个小工具集后面做网络排查直接调。比如把 ICMP 探测封装成带超时和重试的函数把 RIP 模拟的拓扑用配置文件描述这样换个拓扑不用改代码。一个具体技巧用argparse给脚本加命令行参数让实验代码变成可复用工具。# ping_tool.py import argparse from scapy.all import IP, ICMP, sr1 def probe(dst, count3, timeout1): for i in range(count): pkt IP(dstdst) / ICMP(seqi) resp sr1(pkt, timeouttimeout, verbose0) if resp: print(fseq{i} 回复来自 {resp.src}, TTL{resp.ttl}) else: print(fseq{i} 超时) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(dst, help目标地址) parser.add_argument(-c, typeint, default3, help探测次数) args parser.parse_args() probe(args.dst, args.c)这样python ping_tool.py 8.8.8.8 -c 5就能直接跑比每次改代码强。参数-c控制次数timeout在函数里默认 1 秒网络差的环境可以调大。验证方法上我一般会做两件事一是用 Wireshark 对照抓包确认程序发出的报文和预期一致二是构造异常场景比如拔网线、改路由表看程序的行为是否符合协议规范。能通过异常测试的代码才算真正理解了协议。最后说个血泪教训实验报告里的结果分析别只贴截图要写清楚我预期看到什么、实际看到什么、差异的原因是什么。我当年第一次做 RIP 实验收敛慢的问题没分析被扣了不少分。后来养成习惯每个实验都留一份异常记录反而成了后面复习和面试的素材。希望帮到你。本文还有配套的精品资源点击获取
返回列表