恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

基于Python在UDP上实现可靠数据传输:停等、GBN与SR协议解析

  • 首页
  • 资讯中心
  • /
  • 基于Python在UDP上实现可靠数据传输:停等、GBN与SR协议解析

相关资讯

C语言1000个最小可运行案例:嵌入式与系统编程实战库 2026/10/19 10:58:53
.git目录泄露防护指南:源码泄露风险与Gitjacker工具实战解析 2026/10/19 10:58:53
Trade.dll与TradeX.dll技术解析:Windows量化交易本地接口实战指南 2026/10/19 10:53:53

最新资讯

CNN并行计算全解析:数据并行、模型并行与张量并行的实战策略
AI数字员工实战:用Moltbot搭建自动化工作流的完整指南
RNN、LSTM、BiLSTM三部曲:从原理到实战的序列建模指南
GitHub周榜风向:AI代码审查、向量检索与本地优先工具解析
深度学习中的CNN并行计算:四大并行策略、工程实现与避坑指南
软件I2C模拟16位寄存器地址的读写实现与调试指南

今日推荐

OpenCowork 实测:支持本地文件、飞书机器人的 Windows AI 助手(只需配置 Token)
GPT-6 Sol 与 Luna 怎么选?Chat、Work、Codex 版本差异与 TaoToken 接入实测
openclaw.net 集成 ElBruno.MempalaceNet 记忆系统:把 MCP 配置改到 TaoToken 的完整实践

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

基于Python在UDP上实现可靠数据传输:停等、GBN与SR协议解析

发布时间:2026/10/19 10:58:53
基于Python在UDP上实现可靠数据传输:停等、GBN与SR协议解析 简介基于Python实现可靠数据传输协议的课程设计资源面向计算机网络专业学生及需要掌握可靠传输原理的开发者覆盖UDP、停等协议、GBN、SR等核心内容。资源包共14个文件以8个Python源码为主另有3个文本数据文件、1份Word实验报告、1份Markdown说明及1个License协议源码按protocol、server、client、config等模块组织整体压缩包仅723KB结构紧凑清晰。资源包由s1t16整理发布已有596人学习参考。通过这套资料读者可逐步完成基于UDP的停等协议单向可靠传输设计、模拟数据包丢失以验证协议有效性并进一步扩展为双向通信与C/S文件传输应用同时也能掌握GBN协议的实现方法理解从GBN到SR协议的改进思路。配套的实验报告和说明文档记录了目的、过程与关键点适合作为课程设计、实验复现或面试准备的可操作参考。1. 在计算机网络里UDP 常被看成一种“发完就忘”的黑匣子可靠数据传输协议听起来像是它不该管的事。但基于 Python 实现可靠数据传输协议课程设计编号 100010493恰恰是在 UDP 之上硬生生做出了停等、GBN 和 SR 三套可靠传输机制。反直觉的地方在于可靠性并不是传输层内置的属性而是发送方、接收方的状态机、定时器、缓存和重传策略“拟态”出来的。UDP 把路由和交付的脏活留给上层这种故意简陋的接口正好让我们亲手把可靠性的每一颗螺丝装上、拧紧。搞懂它也就等于拆掉了 TCP 那层黑匣子之后再去看 TCP 的序号、ACK、窗口和重传处处都是熟悉的影子。2. 停等协议一个包在途为什么还要序号位2.1 单包在途的隐性问题重复 ACK 与乱序包停等协议的精髓是“一次只发送一个数据分组收到确认后再发下一个”。但这句话很容易被误读成“协议永远不会出歧义”。实际上一个数据包在途时发送方可能因为 ACK 丢失而决定重传接收方这时候收到的就是一个重复分组。如果不给分组加序号接收方根本区分不了“这是新数据”还是“刚才那份重传的旧数据”重复投递就会发生。这也是为什么哪怕窗口只有 1停等协议也必须给序号留出至少一个 bit序号 0 表示第一轮序号 1 表示第二轮两轮交替接收方就能把旧包拒之门外。ACK 本身也会重复。接收方收到重复的数据包时常见的做法是丢弃它并再次发送上一次的 ACK。这样发送方就能从重复 ACK 中意识到“接收方已经拿到过了”而不会继续傻等。但要注意重复 ACK 不等于“新 ACK”。在停等协议里发送方必须把 ACK 中的序号与当前期望确认的序号做比较只有一致时才推进发送状态。这里最容易翻车的细节是有人把“收到任意 ACK”当作“重传成功”结果数据一直停留在同一轮界面永远卡在同一个序号日志里全是同一个包在来回飞。2.2 停等协议的吞吐量瓶颈利用率公式与 UDP 的实际交互停等协议最大的代价不是代码复杂而是信道利用率低得让人心疼。设链路带宽为 R数据包大小为 L往返时间为 RTT发送一个包的时间是 L/R那么理想利用率可以近似写作 L/R 除以 L/R RTT。直觉比公式更重要总时间被分成了“发送用时”和“等待往返用时”而真正在传输数据的时间只有前一半。RTT 是 30ms发送一个 1500 字节包只需不到 1ms 时利用率不到百分之几。所以在局域网课程设计里停等看起来“还行”一旦把丢包模拟和延迟加进去窗口迟迟不推进吞吐量立刻露馅。从 UDP 的实现角度看停等协议其实是对“不可靠信道”做了一层时序同步。发送方调用 socket.sendto() 后立即启动一个超时计时器然后阻塞在 socket.recvfrom() 上只有在超时或收到有效 ACK 时才恢复。UDP 本身就允许接收方收到乱序、重复、丢失的报文停等协议做的正是把这些混沌事件收敛成两种状态超时重传或确认推进。很多人写代码时总想先封装“可靠的发送函数”但真正的关键是先搞清楚 socket 的 read 行为是阻塞还是非阻塞——这决定了后面所有状态机的驱动方式。2.3 怎么在 UDP 里模拟丢包random 模块与丢包位置的选择模拟丢包不能用 TCP 的语义因为我们要的是“UDP 丢”不是“让上层重传”。课程设计里常见的实现思路是在接收方收包后立刻用 random.random() 抽签# receiver.py 中的丢包模拟 import random LOSS_RATE 0.1 # 10% 丢包率 def receive(raw): if random.random() LOSS_RATE: return None # 模拟网络丢弃不投递给上层协议 # 正常解析包校验序号、校验和 ...丢包位置选在“数据包到达接收方时”还是“ACK 包到达发送方时”结论完全不同。只丢数据包看到的是发送方频繁超时重传只丢 ACK发送方也会超时重传但接收方不会重复收到数据。真实实验里更合理的是两个位置都按一定概率丢才能让协议同时应对两路不确定性。此外random 模块的种子建议固定下来设成固定 seed 后同样的丢包序列每次都能复现排查协议 bug 时非常省命。否则每次运行丢包位置都不一样你会陷入“像是好了又坏了”的玄学状态。停等协议在 UDP 上的另一个天然短板是没有拥塞控制它既不会像 TCP 一样因拥塞而减小窗口也不会对超时做复杂的 RTO 估算。用固定超时网络状态好时就“傻等”用太短超时重传风暴又随时可能发生。所以读懂停等协议的代码不是目的意识到“可靠传输的一切复杂度都藏在定时器与序号的交互里”才是进入 GBN 之前最该带走的东西。3. GBN 滑动窗口窗口越大就越可靠吗3.1 累积确认的设计动机ACK 序号的含义停等协议把每个包的确认都变成一次往返瓶颈很明显。GBNGo-Back-N的思路是让发送方一次把窗口里的 N 个分组全发出去不需要等每个 ACK接收方则按序号顺序接收并且不再对每个分组单独回 ACK而是回“累积确认”。所谓累积是指 ACK 序号 k 表示“序号 k-1 及以前的所有分组我都收到了”。这个设计带来两个好处一是大量分组可以用一条 ACK 确认ACK 数量骤减二是接收方不需要为乱序包准备缓存丢一个包后的处理逻辑非常简单。但累积确认的代价也立刻浮现如果序号 2 丢了接收方收到 3、4 时发现“不是下一个想要的”在纯 GBN 里就直接丢弃然后重复回 ACK 1。发送方超时后从 2 开始重发把 3、4 也跟着全部重发一遍。注意这里重发的不是“丢失的那个”而是“从丢的那个开始之后全部”这正是 Go-Back-N 名称的由来。窗口越大可能重传的尾巴越长。可靠性并没有提高只是允许更多的包同时在途而已。窗口大小其实是一笔交易用更高的重传代价换更低的中途等待。3.2 为什么超时事件里只回退到 baseGBN 状态机与窗口推进GBN 发送方维护两个核心变量base最早一个未确认分组的序号和 nextseqnum下一个待发送分组的序号。窗口长度固定为 N发送窗口的覆盖范围是 [base, baseN-1]。收到 ACK 时如果 ack base就说明又有一批分组被连续确认了于是把 base 更新为 ack1。同理当 nextseqnum - base N 时发送方可以继续发直到窗口塞满。超时发生时要从 base 重传到 nextseqnum-1一个都不能少。为什么只回退到 base而不是像某些简化实现那样只重传 timeout 的那个包因为接收方采用的是“仅按序递交”策略base 后面的包即便都到达也可能因为缺了 base 那个包而被丢弃重传它们没有意义。代码骨架大致长这样# sender.py 中 GBN 发送方核心状态 class GBNSender: def __init__(self, N): self.base 0 self.nextseqnum 0 self.N N # 窗口大小 self.timer None def send_more(self): # 只要窗口有空位就尽量连续发送 while self.nextseqnum self.base self.N: self.send_packet(self.nextseqnum) # 调用 UDP sendto self.nextseqnum 1 def on_ack(self, acknum): if acknum self.base: # 累积确认base 直接跳到 acknum 1 self.base acknum 1 if self.base self.nextseqnum: self.stop_timer() else: self.restart_timer()on_ack 里的比较是刻意为之只有当 ACK 序号大于等于 base 时才推进窗口。小于 base 的 ACK比如重复 ACK直接忽略这避免了“旧 ACK 把窗口扯回去”的经典 bug。窗口推进的时机和定时器的重启时机绑定在一起窗口非空时要保证定时器在跑窗口空了之后定时器才可停。这一步没有处理好就会出现“数据全发出了但定时器不会关闭卡死在一个无意义的等待里”。3.3 与停等对比窗口大小、利用率、确认行为把停等和 GBN 放在一起看其实是一张资源置换表协议发送窗口接收方缓存确认方式丢包重传粒度停等1无单个确认只重传当前一个GBNN不需要累积确认从丢失处之后全部重传SRNN单独确认仅重传真正的丢失包从利用率角度GBN 的发送时间比停等多乘了个 N发送方在等待 ACK 的间隙里还能继续发 N-1 个包管道被填满利用率大幅提高。但“窗口越大越可靠”是错误直觉窗口只是决定网络里同时在途的分组数量和丢包重传与否没有关系。实际工程里窗口往往受限于接收方缓冲区也受限于序号空间所能表达的范围——这个约束到 SR 一节会变成硬性条件。3.4 GBN 的定时器选择统一计时还是逐包记账经典的 GBN 发送方只维护一个定时器因为超时后要重传整个窗口不需要精确到单个包。但很多 Python 课程设计为了省事会把超时时刻记在“每个已发送的包”上这就滑向 SR 的实现思路了。如果只想做纯 GBN一个 timer 加一个 timeout_handler 就够了每次超时把 base 到 nextseqnum-1 逐包重发再重启同一把计时器。这段逻辑实现不难难的是别把重传时的 nextseqnum 也推进了否则新一轮发送会间接扩大窗口。有的同学就在这里踩了坑重传时继续调用 send_packet() 并推进 nextseqnum导致同一个序号被发了两次接收方顺序全乱。4. SR 选择重传用接收缓存换取只重传丢的那个包4.1 从 GBN 的设计漏洞说起失序分组的价值GBN 的致命伤是“错过一个包后面全陪葬”。在网络状况差、连续丢包不少的场景里GBN 会把大量已被正确接收的数据重新发一遍浪费的带宽可能超过真正的新数据。SRSelective Repeat的出发点非常简单既然接收方已经收到了 1、2、4缺 3那么 3 之前的包没必要重发4 也不必作废。接收方应该把 4 先缓存起来等 3 补上之后把 3、4 按顺序一起交给上层。这件事做起来的代价就是接收方必须拥有一个“窗口大小”的缓存区并且每个缓存单元要有“已收/未收”的标记。每当收到一个窗口内序号的分组先把它放到对应 slot如果恰巧是窗口下沿那个空缺再把连续的几个分组一次性交付并且右移窗口。这比 GBN 接收端只维护“期望序号”复杂了不止一个量级但也正是缓存的存在让“选择重传”成为可能。资源里的 SR 部分通常会把 receiver 的缓存逻辑单独抽成一个类与发送方窗口互相对应report.docx 里的状态机图也会比停等、GBN 的密得多。4.2 SR 的窗口移动条件为什么接收窗口必须小于序号空间一半SR 看起来只是把缓存加上去真正隐藏的雷在序号空间大小。假设序号用 k 位表示序号空间是 2^k。发送方窗口大小不能超过 2^(k-1)接收窗口也一样。原因可以做一个极简例子序号空间为 40,1,2,3发送窗口取 3。发送方发 0、1、2假设 0 丢失1、2 都被接收方缓存并 ACK。发送方随后收到 ACK 1 和 ACK 2认为 0 超时重传 0同时窗口滑动允许发送 3 和新的 0。问题来了网络上很可能同时存在“旧 0”的重传包和“新 0”的首发包两者序号相同接收方又分别用哪个对应窗口呢如果接收窗口大小也是 3旧 0 和新 0 都落在窗口内接收方根本无法区分谁是旧、谁是新。限制条件 W 2^(k-1) 的来历就在这里新旧窗口在序号空间上不会重叠因此即使新序号值和旧序号值相同接收方也能根据当前窗口下沿知道它是“上一轮”还是“下一轮”。常见的实现里发送方维护一个长度为 2^k 的循环数组接收方则用 base 的模运算去匹配。千万别省这一步否则 SR 在连续丢包时会逐渐出现“数据错位”的诡异 bug而且越改越乱。这个约束不是理论洁癖而是协议不出错的最低保险。4.3 缓存排序与 ACK 风暴重复 ACK 的真正价值SR 接收方收到任何窗口内的重复分组时不能像 GBN 那样直接丢弃而应重新发送该分组的 ACK。这是因为发送方对某个包可能只有一个定时器ACK 一旦丢失发送方只能靠超时重传。如果接收方遇到重复包却只是静默丢弃发送方下一次超时会把同一个包再传次数无限增加。所以 SR 接收方的处理策略通常是“无论新旧只要落在窗口内就回 ACK即使已经收过也再回一次 ACK”。这段逻辑用 Python 写出来大概是# receiver.py 中 SR 接收方的缓存与 ACK 处理 class SRReceiver: def __init__(self, win_size): self.win_size win_size self.buffer {} # 保存未递交的包 self.base 0 # 窗口下沿 def on_packet(self, pkt): seq pkt.seq if self.base seq self.base self.win_size: if seq not in self.buffer: self.buffer[seq] pkt.data self.send_ack(seq) # 即使是重复包也发 ACK while self.base in self.buffer: # 连续序号到齐递交并移窗口 self.deliver(self.buffer.pop(self.base)) self.base 1on_packet 里的 send_ack 放在 if 内部条件不满足seq 在窗口外才不回。窗口外又分为“太旧”和“太新”。太旧的包可能来自网络延迟返回 ACK 也无妨但缓存它没有意义太新的包说明接收窗口还没滑过去此时可以发 ACK 提醒发送方却不应缓存否则窗口失去控制。4.4 定时器策略逐包计时与单个计时器的实现取舍SR 发送方理论上要为每个在航的包维护一个定时器才能做到“只重传超时的那一个”。严谨实现里可以给发送窗口内每个序号维护 timeout 时刻每次循环扫描所有超时者逐一重发。但 Python 里开销并不大课程设计完全可以做成一个列表# sender.py 中 SR 发送方的超时记录 self.send_time [None] * MAX_SEQ # 每个序号最后一次发送的时间 self.timeout 0.8 def check_timeout(self, now): for seq in range(self.base, self.base self.N): sent self.send_time[seq] if sent is not None and now - sent self.timeout: self.send_pkt(seq) # 只重传这一个包 self.send_time[seq] now这段实现看起来比 GBN 的“整体重传”更精细但它的前提是接收方真的为每个包单独 ACK。否则发送方误以为对方缓存了乱序包实际上对方早就丢了重传依然无效。所以 SR 的复杂度不在代码行数而在“发送方重传哪些包”和“接收方缓存哪些包”必须配对正确。另外注意窗口下沿的移动规则发送方只有收到对窗口下沿分组的 ACK 才会右移 base如果只是收到后来分组的 ACKbase 并不会动。这和 GBN 的“acknum base 就滑动”完全不同。很多实现把 base 更新写成 max(base, ack1)在 SR 里不成立因为 SR 的 ACK 是单独确认它不能代表前面所有包都到齐。这个差异是协议从 GBN 迁到 SR 时最容易踩的暗坑。5. 协议调试常见坑与排查为什么你的协议一跑就崩课程设计最容易出问题的位置集中在丢包模拟、定时器、双向 socket 读写的交叉点上。问题往往不是协议理论没看懂而是实现里的某个状态变量被不同的分支各改了一次导致协议在概率性事件面前突然失灵。下面几类是调试时最常见的失败模式每条按现象、原因、解决的方式记录可以直接对照代码排查。5.1 丢包率飙升与 ACK 风暴现象把 LOSS_RATE 设为 30% 后协议吞吐量直接掉到零发送方日志里全是“重传”接收方日志里也全是“重复 ACK”CPU 居高不下。原因丢包率太高时超时重传本身就制造新的拥塞和丢包协议陷入重传风暴。再叠加 SR 里“接收方对重复包重发 ACK”的行为ACK 通道比数据通道还忙反而放大了丢包概率。解决先用 1% 或 5% 的丢包率做功能验证正确性确认后再研究高丢包下的行为。同时给数据包和 ACK 包设置相互独立的丢包概率别把两条方向的事件混为一谈。否则同一个随机数既丢数据又丢 ACK问题复杂度会成倍上升。5.2 超时设置在 UDP socket 上的“假死”现象超时时间明明设了 0.1 秒实际跑起来却像被阻塞一样迟迟不触发重传。原因可能是用了 socket.settimeout(0.1) 后在 recvfrom 里超时会抛 socket.timeout但没有在 except 分支里处理另一个更隐蔽的原因是计时逻辑放在了“收到 ACK 后”这个事件里而网络长期没有事件计时器自然没有机会跳闸。解决把超时检查放在主循环的最顶部每次循环先看当前时间与 send_time 的差值再决定是否 recvfrom。这样无论有没有事件超时都会如期生效。同时要区分“超时重传”和“心跳检测”不要在重传函数里做阻塞式读否则计时器永远到不了超时那一刻。5.3 双向传输时的互相卡死现象把停等协议改成双向传输后A 和 B 同时发送数据结果两边都停在“等待 ACK”一条消息也送不出去。原因A 把“自己的数据”和“对方的 ACK”都放在同一个 socket 接收逻辑里而 B 也在等待 A 的 ACK。如果设计成“整包接收后再判断是数据还是 ACK”双方都要等对方先回应形成等待环。解决用独立线程分别处理“发送”和“接收”或者用 select 同时监听 socket 的读事件和用户输入事件。无论用哪种方式接收解析必须先做一次 type 判断把 ACK 和 DATA 分到不同的处理分支。这类问题用 Wireshark 看最容易两边都在发包但包的类型全是 DATAACK 一个都没出现说明发送和接收逻辑被同一把锁卡住了。5.4 接收窗口滑错把重复 ACK 当成新推进现象SR 实现中接收方偶尔会跳过一些序号之后的乱序包怎么都缓存不上最后上交的数据出现乱序。原因接收方把“seqbase 的分组收到”和“base 窗口右移”直接划等号。当收到重复的 base 包时它又执行了一次窗口移动导致窗口越滑越快把尚未收到的序号甩在了窗口后面。解决守好两条规则只有连续缓存中的下一个序号到达并标记“已收”时窗口下沿才移动重复包只重发 ACK不移动窗口。可以在 receiver 里用 debug 日志打印 base 变化每移动一格都核对一遍当前缓冲区头部是否为空。5.5 序号回绕与旧包误判现象发送完一轮固定范围的序号后序号回到 0接收方把循环回来的旧包当新包数据顺序错乱缓存里出现两个一模一样的 key。原因序号空间有限窗口大小超过了 2^(k-1)且接收方没有用 base 判断序号是否落在当前窗口内。旧一轮的包和新一轮的包在序号值上完全相同无法仅凭序号区分。解决先确定序号位数 k并把窗口大小限制在 2^(k-1) 以内接收方缓存前先检查“seq 是否在从 base 开始的窗口范围内”不在就放弃或仅回 ACK。这个顺序不能省否则序号回绕必然产生歧义。用 Python 的 int 做序号时尤其要小心int 理论上无限大但 UDP 报文里的序号字段长度是定死的回绕迟早会发生。6. 从停等到 SR可靠性协议设计是“用哪里换哪里”的取舍6.1 三类协议其实是三种资源置换取舍维度停等GBNSR时间代价高中低重传带宽代价低高最低接收方复杂度低低中高适用场景信道极好、极低速丢包少、求简单丢包可见、带宽昂贵停等是用时间换简单GBN 是用重传带宽换接收方缓存SR 是用发送方多定时器和接收方缓存换实际吞吐量。每一种协议都在回答同一个问题当某一种资源稀缺时你愿意用另一种资源去补。网络课程设计里没有绝对“最好”的协议只有与丢包率、RTT、内存约束匹配最好的取舍。6.2 如何用这份资源做一次可复现的验证把这份资源里的 server.py 和 client.py 拉下来先不改协议只通过 util/config.py 调整 LOSS_RATE、RTO 和窗口大小记录不同参数下的有效吞吐量和重传次数。你会发现停等和 GBN 的曲线有一个明显的交叉点丢包率低时GBN 的累积 ACK 足够高效丢包率升高时GBN 的重传风暴让吞吐量跳水SR 的缓存和按需重传优势才真正显现。跑完这组对比才算真正理解了可靠传输协议为什么会在教材里按这个顺序演进。我当年做类似课程设计时为了省事把 SR 接收方的窗口滑动按 GBN 的累积确认来写整整两个晚上都在跟乱序数据搏斗。后来强迫自己先画状态转移图、再动代码一次通过。从那以后每写一个协议状态机我都会先把“哪些事件推进窗口、哪些事件只影响缓存”列清楚再写循环。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号