ARTICLE DETAIL

资讯详情

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

Python实战:Winsock、ARQ、RIP、HTTP五个计算机网络实验全解析

Python实战:Winsock、ARQ、RIP、HTTP五个计算机网络实验全解析 简介这份资源是黑龙江大学计算机网络课程的实验报告与配套源码合集面向正在学习计算机网络、需要动手实践协议原理的高校学生与自学者。内容围绕Winsock编程、HTTP协议简单实现、停止等待ARQ算法、RIP路由协议以及ping、traceroute等常用网络命令展开用Python将抽象协议落地为可运行代码帮助读者在编程中理解网络通信机制。压缩包共24个文件以11个py源码、5个doc实验报告、4个html页面为主另含少量pyc、txt与png整体约341KB体量轻便便于按实验模块对照阅读。目前已有1081人学习下载。源码格式规范、注释清晰实验报告与代码相互印证读者可据此复现TCP通信、HTTP请求响应、ARQ重传与RIP路由更新等过程并借助网络命令排查思路加深对协议行为的认识适合作为课程实验参考与网络编程入门练习材料。1. 从一份高校实验包说起Python 把 Winsock、ARQ、RIP、HTTP 全串了一遍很多人学计算机网络课本翻到传输层就开始犯困谢希仁第八版里那些状态机、窗口、路由收敛考试能背真让你写个 socket 就卡壳。这份黑龙江大学的计算机网络实验报告加源码恰好是拿来补这个断层的。它用 Python 把五个实验串成一条线实验一是 Winsock 编程实验二是停止等待 ARQ 模拟实验三是 RIP 路由协议模拟实验四是一个简单 HTTP 实现实验五配了 doc 报告。源码和报告都在说明.txt 交代了目录结构。适合两类人正在上这门课、需要交实验报告的学生以及想用最短路径把网络协议从纸面落到代码的转行者。下面我按“能跑起来、能改参数、能看懂报告”的顺序拆一遍。2. Winsock 与 ARQ先让两个进程说上话再让它们说对2.1 Winsock 编程在 Python 里到底对应什么实验一叫 Winsock 编程但源码是 Python所以别被“Winsock”这个词吓住。Winsock 是 Windows 上的套接字接口规范Python 的socket模块在 Windows 下底层就是走 WinsockAPI 语义和 Berkeley Sockets 基本一致。也就是说实验一真正要你掌握的是socket()、bind()、listen()、accept()、connect()、send()、recv()这一套调用顺序以及 TCP 三次握手在代码里落在哪几行。我一般会先确认环境避免后面所有实验都跑不起来python --version # 建议 3.8 及以上源码里用到了 f-string 和部分类型注解 pip list | findstr socket # socket 是标准库不需要装这条只是确认环境没被污染逻辑说明Python 标准库自带socket不需要 pip 安装网上那些“python安装教程”里让你装一堆第三方包的说法在这里不适用。参数说明python --version用来确认解释器版本低于 3.6 的话 f-string 会直接报语法错误这是新手最常见的翻车点。服务端骨架大致是这样源码里实验一就是这个结构import socket HOST 127.0.0.1 # 本机回环地址实验阶段不要用 0.0.0.0 暴露到局域网 PORT 8888 # 端口要大于 1024避免和系统服务冲突 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) # 绑定地址和端口 s.listen(1) # backlog1实验够用 conn, addr s.accept() # 阻塞等待客户端连接 print(connected by, addr) while True: data conn.recv(1024) # 每次最多收 1024 字节 if not data: break # 对端关闭连接 conn.sendall(data) # 回显逻辑说明AF_INET指定 IPv4SOCK_STREAM指定 TCP。accept()返回一个新的连接套接字conn和客户端地址addr注意后续收发都用conn而不是监听套接字s这是实验一里最容易写错的地方。参数说明recv(1024)的 1024 是单次接收上限不是总长度TCP 是字节流一次recv拿到的可能只是半条消息所以真实项目里要自己拼包或加长度前缀实验阶段回显短字符串问题不大。客户端对应地connect((HOST, PORT))然后sendall再recv。跑的时候先起服务端再起客户端顺序反了客户端会直接抛ConnectionRefusedError这不是代码错是没人监听。2.2 停止等待 ARQ把“等确认再发下一条”写成循环实验二是 ARQ 模拟停止等待协议的核心就一句话发一帧等 ACK超时没等到就重传。源码里把它写成一个循环配合超时和序号。理解这个实验的关键是分清“发送方状态”和“接收方状态”以及超时定时器怎么模拟。import time TIMEOUT 1.0 # 超时阈值单位秒 LOST_PROB 0.3 # 模拟丢包概率方便观察重传 def sender(frames): seq 0 while seq len(frames): frame frames[seq] print(f发送帧 seq{seq}: {frame}) ack wait_ack(seq) # 等待对应序号的 ACK if ack: seq 1 # 收到确认窗口前移 else: print(f超时重传 seq{seq}) # 没收到序号不变重发 def wait_ack(expected_seq): time.sleep(0.2) # 模拟往返时延 import random if random.random() LOST_PROB: return False # 模拟 ACK 丢失 return True逻辑说明seq只有在收到 ACK 后才加一超时分支里seq不动下一轮循环自然重发同一帧这就是停止等待的本质。参数说明TIMEOUT设太小会频繁误重传设太大吞吐上不去实验里 1 秒够用LOST_PROB是人为加的丢包模拟真实网络里这个值由链路质量决定调大它就能看到重传次数明显上升报告里可以拿这个做对比数据。这里有个细节值得在报告里写清楚停止等待的信道利用率是1/(12a)其中a 传播时延/发送时延。源码只模拟了逻辑没算利用率但报告里补上这个公式实验分基本就稳了。3. RIP 与 HTTP一个管路由收敛一个管请求响应3.1 RIP 距离矢量算法路由表怎么一轮轮收敛实验三是 RIP 模拟。RIP 是距离矢量协议每个路由器只知道“到某个网段要几跳、下一跳是谁”然后周期性把整张路由表发给邻居。收到邻居的表后对每一条路由做“跳数加一”如果比自己表里的更短就更新否则忽略。这个“加一比较”就是距离矢量的全部精髓。源码里通常用一个字典表示路由表键是目的网段值是(距离, 下一跳)。核心更新逻辑def update_routing_table(my_table, neighbor_table, neighbor_name): for dest, (cost, _) in neighbor_table.items(): new_cost cost 1 # 经过邻居到达跳数加一 if dest not in my_table or new_cost my_table[dest][0]: my_table[dest] (new_cost, neighbor_name) # 更优则更新 return my_table逻辑说明cost 1是距离矢量的关键动作邻居说到目的地要 2 跳那经过邻居就是 3 跳。参数说明neighbor_name记录下一跳方便后面还原路径RIP 规定最大跳数 1516 表示不可达源码里如果没写这个上限收敛测试时可能出现计数到无穷的问题这是可以在报告里点出来的加分项。跑收敛测试时我一般会手动构造三到四个路由器让它们互相交换几轮打印每轮的路由表观察某个网段的跳数是不是逐轮下降然后稳定。稳定后再断开一条链路看它能不能重新收敛。这个“断开再收敛”的过程就是 RIP 慢收敛问题的直观演示报告里配上每轮的表比干讲理论有说服力。3.2 HTTP 简单实现GET 请求从拼报文到解析响应实验四是 HTTP。这里要区分两件事写一个能处理 GET 的简易服务器和写一个能发请求拿响应的客户端。源码里两者都有。HTTP 报文是纯文本请求行加头部加空行理解了这个格式自己用 socket 拼一个请求完全可行。import socket def http_get(host, path): s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, 80)) # HTTP 默认 80 端口 request fGET {path} HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n s.sendall(request.encode(ascii)) # 报文必须是 ASCII 编码 response b while True: chunk s.recv(4096) if not chunk: break response chunk s.close() return response.decode(utf-8, errorsignore)逻辑说明请求行GET 路径 HTTP/1.1Host头在 HTTP/1.1 里是必需的Connection: close让服务器发完就关连接这样客户端靠recv返回空来判断结束省去解析Content-Length。参数说明encode(ascii)是因为请求行和头部按规范是 ASCII响应体可能是 UTF-8解码时加errorsignore防止个别字节报错中断。服务器端则要解析请求行拿到路径读文件拼响应头HTTP/1.1 200 OK加Content-Length再发正文。实验四的报告里通常会让你对比“有 Content-Length”和“没有”时客户端行为的差异这个对比值得做。4. 避坑与排查这几个地方我踩过你别再踩4.1 端口被占用服务端起不来现象运行服务端脚本直接抛OSError: [WinError 10048]提示地址已被使用。原因上一次运行的服务端进程没退干净或者 8888 这类常用端口被别的程序占了。解决换一个 1024 以上的端口或者在 Windows 上用netstat -ano | findstr 8888找到占用进程的 PID再到任务管理器结束它。我一般实验时端口从 8888 起每换一个实验加一避免互相干扰。4.2 先起客户端后起服务端连接被拒现象客户端报ConnectionRefusedError: [WinError 10061]。原因TCP 连接需要服务端先listen客户端connect时如果没有监听者系统直接拒绝。解决严格按“服务端先跑、客户端后跑”的顺序如果要做自动化测试在客户端connect外面套一层重试循环隔 0.5 秒重试几次等对端起来。4.3 ARQ 里把超时当成丢包重传逻辑写反现象模拟时明明没丢包却一直重传或者丢了包反而不重传。原因wait_ack的返回值判断写反或者超时后错误地把seq也加了 1。解决记住停止等待的铁律——只有收到 ACK 才seq 1超时分支里seq绝对不动。调试时把每次发送和收到的 ACK 序号都打印出来一眼就能看出错位。4.4 RIP 更新时忘了跳数上限出现计数到无穷现象断开一条链路后某个不可达网段的跳数一直往上涨收敛不了。原因距离矢量协议在没有水平分割和最大跳数限制时坏消息传得慢会互相“喂”错误路由。解决在更新逻辑里加if new_cost 15: continue把 16 当不可达报告里可以进一步提水平分割和毒性逆转作为改进方向。4.5 HTTP 响应中文乱码现象客户端拿到的网页正文里中文变成乱码。原因响应头里的Content-Type没带charset或者解码时用了错误的编码。解决服务器端在响应头写Content-Type: text/html; charsetutf-8客户端解码时优先从头部读 charset读不到再默认 UTF-8。实验阶段统一用 UTF-8 最省事。5. 把五个实验串成一份能交的报告我的验证习惯实验报告 doc 和源码是配套的但报告不是把代码贴一遍就完事。我的习惯是每个实验都留一组“对照数据”这样报告才有说服力。实验一记录服务端和客户端各自的收发日志证明连接建立和回显正确实验二把LOST_PROB分别设成 0、0.3、0.6 跑三组统计重传次数画一张丢包率对重传次数的表实验三打印每一轮收敛后的路由表再断开一条链路记录重新收敛的轮数实验四用客户端请求一个本地 HTML 文件把原始响应报文的前几行贴进报告标出状态行、头部、空行、正文的分界。实验关键参数建议记录的数据实验一 Winsock端口、缓冲区大小连接建立日志、回显内容实验二 ARQ超时阈值、丢包率重传次数、总发送帧数实验三 RIP最大跳数、邻居数每轮路由表、收敛轮数实验四 HTTP端口、Content-Length原始请求与响应报文实验五 报告无上述数据的汇总与结论验证源码能不能跑通我一般按这个顺序走一遍先确认 Python 版本再单独跑实验一的服务端和客户端通了再跑实验二看重传日志然后跑实验三看路由表收敛最后跑实验四用浏览器或客户端请求一次。哪一步报错就停在哪一步排查不要五个一起跑否则日志混在一起根本分不清是谁的问题。还有一点源码里的注释是理解作者意图的捷径尤其是 ARQ 和 RIP 这两个逻辑型实验注释往往写明了状态变量的含义。读代码时先把注释过一遍再对着上面那些参数表改一遍值观察输出变化比死记协议定义有用得多。这份资源的价值就在这儿它把课本上分开讲的四个协议用同一门语言、同一套调试方式串起来了跑通一遍网络编程的门就算推开了。希望帮到你。本文还有配套的精品资源点击获取
返回列表