计算机网络 14:怎样在不可靠信道上传完整内容,ARQ、序号、计时器与滑动窗口
接收端已经得到某块数据,但返回的确认丢了,发送端应该怎么办?不再发送,可能遗漏真正丢失的数据;再次发送,又可能让接收端重复处理。仅靠发送一次或固定重发几次,都无法同时说明完整性与重复交付问题。
第 13 篇展示了 UDP 数据报边界不提供可靠交付。本篇把数据块、确认、计时器、窗口和接收缓存组合成一个有界教学协议。实验采用确定性事件信道,不发送实际网络报文;它不是 TCP,也不声称实现 TFTP。
先定义成功和允许失败的条件
输入是有限字节串,按固定方式划成有限块,每块有唯一编号。接收成功要求按照原顺序输出相同字节,不能少块、重复交付或靠最终排序掩盖已经发生的错误处理。发送成功则要求全部块都收到有效确认。
这两个状态需要分别记录。接收端拿齐内容时,最后一个 ACK 仍可能丢失;发送端因而不能立即知道接收成功。协议应允许接收端继续回应重复数据,同时让发送方在有界尝试后明确失败。永久故障下,不能承诺总能完成传输。
本篇只处理同一传输内的固定编号,不考虑进程重启、旧会话报文、序号回绕和恶意输入。实际协议需要传输身份、生命周期和资源限制,不能把本例的整数编号直接用于无限运行的服务。
确认必须对应某份数据
发送端给 DATA 标上块号,接收端用 ACK 返回已经接收的块号。ACK 的含义是本教学接收状态接受了该块,不是业务写盘、交易提交或副作用执行成功。确认语义一旦改变,发送端可以释放什么状态也会随之改变。
RFC 1350 §2、§4–5提供了基础 TFTP 的停等、块号和确认实例:发送一个块,等待对应确认,再推进下一块。本篇借此说明机制,不采用 TFTP 的报文格式、传输建立和结束规则。
如果一个块的数据丢失,接收端不会因未见过的内容发出有效确认;如果数据到了而 ACK 丢失,发送端同样看不到确认。发送端的这两种观察可以相同,因此它不能只根据等待超时定位丢失方向。
计时器触发重新发送,不证明丢包
每个尚未确认的块记录最近发送时刻。超过设定等待时间后,若还没达到尝试上限,就重新发送同一编号与同一内容。编号不应随着重传改变,否则接收端无法将它识别为旧数据。
等待太短,会把延迟误判成需要重传;等待太长,会延后缺失恢复。本篇采用固定模拟时间刻度,只用于复现事件顺序,没有估计实际 RTT,也没有测得适用于任何网络的 RTO。
重复 ACK 不必触发重新发送。它可能只是重复 DATA 的响应,若双方对每个重复确认都追加发送,就可能扩散无用流量。RFC 1123 §4.2.3.1针对 TFTP 的重复报文问题规定了相应修正。本模型收到已经处理过的 ACK 只保持确认状态,由未确认块的定时器决定重传。
尝试上限使用“最多发送四次”的口径,包含首次发送,因而最多重传三次。这与“最多重试四次”不同。达到上限后仍缺少确认,应返回失败及尚未确认的块,而不能把程序结束写成传输成功。
窗口约束的是一段编号范围
停等在上一块确认之前不发送下一块,等待期间不能利用后续发送机会。窗口允许同时保留多块未确认数据,但需要限定哪些编号可以进入信道。
设最早尚未确认的块为 base,窗口为 W,允许新发的编号满足 base <= seq < base + W。例如 W=3、base=0 时可以发送 0、1、2。若先收到块 2 的 ACK,而块 0 仍未确认,不能只因未确认数量减少就无限发送后续块;最早缺口仍限制窗口位置。
当 base 对应的块得到确认,向前越过连续已确认块,窗口才能继续滑动。这使接收缓存需求与发送进度建立边界。固定窗口也限制了本实验状态规模,但不等于已经具备真实接收端流量控制或网络拥塞控制。
RFC 7440 §3–4给出 TFTP 的窗口扩展,采用连续发送一窗后等待末块确认的方式。本篇选择每块独立确认、只重传未确认块的教学规则,两者不能混称同一算法。允许多个在途块是共同概念,确认与恢复细节仍须分别定义。
接收缓存与去重是两项约束
乱序块到达时,接收端可以先保存在按编号索引的缓存中。只有当前应交付编号已经存在,才取出它并推进交付位置。块 2 提前到达不能让块 1 的位置凭空消失;缓存之后还需要检查连续性。
对于重复 DATA,接收端通常还应再次确认,因为上一次 ACK 可能丢了;但它不能再次交付已经处理过的内容。“重复确认”和“重复业务处理”需要分开。否则为恢复确认而发出的重传,会变成重复执行。
去重也不能只放在最后输出阶段。如果错误接收端先把重复内容交给应用,事后再用字典按编号重组出一份正确文件,仍没有撤销已经发生的重复交付。反例必须保留实际交付事件与输出长度,不能只比较一个经过修饰的最终集合。
完成之后仍有确认问题
假设接收端拿到最后一块就立即退出,最后一个 ACK 又恰好丢失。发送端随后重传最后一块,却再也得不到响应,最终可能报告失败,虽然接收端已经拥有完整内容。
本模型在发送端未完成或未达到失败上限之前,继续处理接收与确认事件。这种行为只在有界模拟中成立;实际实现需要决定结束后的保留时间、状态清理和传输重启策略。不能从单次有限实验推出双方对完成状态具有无限期一致认知。
RFC 8085 §3.1.1、§3.3还要求考虑重传歧义和网络负载。重传同样消耗链路资源,固定窗口和固定上限只是教学边界,不是可直接部署到公网的拥塞控制方案。
可复现模型怎样安排事件
素材包括 教学协议程序、事件记录和 实验说明。Python 3.11 或更新版本可以直接运行,不需要第三方库或网络权限:
1 | |
最后一条应以状态码 2 拒绝未知参数。前两条分别显示帮助和执行四组固定场景。不能加 -O,因为验收依赖程序内置断言。
输入包含六块 block-0; 到 block-5;,每块八字节,总计四十八字节。窗口为 3,每块最多发送四次,定时器为四个模拟刻度。通常 DATA 与 ACK 各延后一刻度到达;事件优先队列按到期刻度及插入序号处理,同刻度的接收先于本轮定时器检查。
组合场景把块 0 首发的到达延迟设为三刻度,使后续块先到。同时丢弃块 1 首次 DATA,复制块 2 首次 DATA,并丢弃块 1、5 首个 ACK。丢失条件按发送或确认次数判断,不使用随机概率,也没有墙上时钟测量。永久故障场景持续丢弃块 2,并保留上述首次 ACK 丢失规则。
仅 ACK 丢失的对照则不延迟或复制 DATA,也不主动丢弃 DATA;一组保留去重,一组关闭去重。这样可保持发送与确认事件相同,直接比较接收应用是否重复得到内容,而不是同时改变信道再归因给去重。
模拟上限还包括刻度 100、处理事件 200 个和待处理队列 64 项。它们是防止实现无界运行的保护,不是网络性能指标。达到保护条件应视为未完成验收,而不是收敛或可靠性证明。
运行结果与反例
2026 年 9 月 20 日运行四组场景,重复运行得到相同 JSON。DATA 事件携带实际字节,接收缓存保存事件载荷,最终输出由交付字节列表拼接。程序没有根据块号从源数据表重新生成接收内容。
| 场景 | 实际应用交付编号 | 输出字节 | 发送端结果 |
|---|---|---|---|
| 组合故障,启用去重 | 0,1,2,3,4,5 |
48,逐字节相同 | 刻度 16 全部确认 |
| 永久丢块 2 | 0,1 |
16,不完整 | 刻度 16 达到块 2 尝试上限 |
| 仅 ACK 丢失,启用去重 | 0,1,2,3,4,5 |
48,逐字节相同 | 刻度 12 全部确认 |
| 同一 ACK 丢失,关闭去重 | 0,1,2,3,1,4,5,5 |
64,内容错误 | 刻度 12 全部确认 |
组合场景中,块 1 发送三次:首次 DATA 丢失,第二次到达后首个 ACK 又丢失,第三次才带来可到达的确认。块 5 发送两次,其余块各一次。重复数据仍得到 ACK,但没有重复交付。
接收端在刻度 11 已见到全部唯一块并完成正确输出,发送端到刻度 16 才收到全部确认。仅 ACK 丢失场景对应两个时点为 7 和 12。这里的差值来自人为事件设置,不能换算成真实网络的毫秒数。
永久丢块 2 时,接收端即使缓存了后续块,也不能跳过缺口将其交付。块 2 在第四次发送仍失败后,到下一次截止时刻停止。块 5 尚未发送,说明窗口确实受缺口约束,而不是不断发送直到耗尽输入。
无去重组保留原始交付列表与 64 字节输出,和正常组的发送、确认事件一致。字段 receiver_complete 只表示所有唯一块都曾到达,不保证应用输出正确;还必须检查 application_matches。后者在这组明确为假,不能只挑选“全部确认”或“全部块到达”字段宣称通过。
验证边界
已验证有限输入、固定故障轨迹下的窗口约束、逐块计时重传、实际载荷缓存、按序交付、重复抑制及有界失败。重复 ACK 不产生额外 DATA,末块首个 ACK 丢失后接收端继续响应,最终发送方完成。输出字节由每次交付事件直接拼接并与源输入比较。
未验证真实 socket、进程调度、实际 RTT/RTO、拥塞控制、恶意 ACK、损坏载荷、重启恢复、序号回绕或并发传输。关闭去重的对照只用于没有人为乱序的 ACK 丢失场景,不能用它分离任意乱序场景中的所有错误原因。下一篇将另行核查 TCP 字节序号和实际报文,不把本例块号当作 TCP 序号。
练习
第一题:窗口为 3,base=0,块 0、1、2 已发,只有块 2 得到确认。此时是否可以发送块 3?若随后收到块 0 的 ACK 呢?
校验:仅确认块 2 时,base 仍为 0,块 3 不在允许范围。随后确认块 0,base 可推进到 1,于是块 3 进入 [1,4);块 1 未确认又限制了进一步推进。这不是简单的“窗口等于剩余未确认数量”。
第二题:接收端输出字节完全正确,但发送端最终达到重试上限。能否认定实现一定错误?
校验:不能。若所有末块确认都丢失,这两种状态可以同时成立。需要检查接收端是否按规则重发 ACK,以及信道是否继续丢弃确认,再判断实现缺陷。它同时说明仅校验最终文件不足以验证发送端完成条件。
参考资料
- RFC 1350 §2、§4–5:TFTP 的停等、块号与确认。
- RFC 1123 §4.2.3.1:TFTP 重复报文处理修正。
- RFC 7440 §3–4:TFTP 窗口扩展的具体规则。
- RFC 8085 §3:UDP 应用的拥塞与可靠性责任。




