自动重传请求(ARQ)基础:从停等到滑动窗口
ARQ 这个词,说白了就是「发出去没收到确认,那就再发一次」。听起来简单,但实际落地时坑不少。我早年做卫星通信项目时,一个包往返要 600 毫秒,选错 ARQ 模式直接导致吞吐量崩盘。今天咱们就把 ARQ 的三种经典形态掰开揉碎讲清楚。
ARQ 工作原理:三要素与两种信号
任何 ARQ 协议都离不开三个核心动作:
- 发送方:发出数据包,启动定时器
- 接收方:收到包后回送确认(ACK)或否认(NAK)
- 超时重传:定时器到期未收到 ACK,发送方重传
这里有个关键点——序号。没有序号,接收方分不清是新包还是重传包。我在项目中见过有人把序号省掉,结果接收方把重传包当新数据处理,整个流都乱了。
核心公式:ARQ 的可靠性 = 序号 + 确认 + 超时重传。缺一个都不行。
两种信号决定了协议效率:
| 信号 | 含义 | 典型场景 |
|---|---|---|
| ACK(确认) | 「包已收到,请发下一个」 | 正常传输 |
| NAK(否认) | 「包丢了或坏了,请重传」 | 检测到错误 |
我的习惯:NAK 不是必须的。纯超时重传也能工作,但加上 NAK 能加速丢包恢复。不过要注意——NAK 本身也可能丢,所以超时兜底不能省。
停止等待协议(Stop-and-Wait)
这是最朴素的 ARQ。发送方发一个包,等 ACK,收到后再发下一个。你想想看,就像你给朋友发,他回一句你才发下一句。
工作流程
- 发送方发序号为 0 的包,启动定时器
- 接收方收到后回 ACK 0
- 发送方收到 ACK,发序号为 1 的包
- 如果定时器到期没收到 ACK,重传当前包
这里有个细节——序号只需要 1 位(0 和 1 交替)。为什么?因为停等协议里,发送方手里最多只有一个未确认的包,0 和 1 足够区分新旧。
我曾经踩过的坑:定时器超时时间设得太短。在 Wi-Fi 环境下,偶尔延迟抖动到 200ms,我设了 150ms 超时,结果疯狂重传,吞吐量直接腰斩。建议超时时间至少设为 RTT 的 2-3 倍。
效率分析
停等协议的效率公式很简单:
利用率 = 传输时间 / (传输时间 + 2 × 传播延迟)
= Tt / (Tt + 2 × Tp)
当传播延迟远大于传输时间时,效率惨不忍睹。举个例子:
| 场景 | 带宽 | 包大小 | RTT | 利用率 |
|---|---|---|---|---|
| 局域网 | 1 Gbps | 1500 字节 | 1 ms | ≈ 1.2% |
| 卫星链路 | 10 Mbps | 1500 字节 | 600 ms | ≈ 0.02% |
看到没?停等协议在高速长距离链路上基本是废的。我当年做卫星项目时,用停等协议传 1MB 文件要 5 分钟,换成滑动窗口后只要 3 秒。
滑动窗口协议(Sliding Window)
滑动窗口的核心思想是:允许发送方连续发多个包,不必等每个 ACK。窗口大小决定了最多能有多少个包「在途」。
窗口机制
发送方维护一个窗口,窗口内的包可以连续发送。每收到一个 ACK,窗口向前滑动。接收方也有窗口,决定哪些包可以接收。
我习惯把窗口想象成「流水线」——停等协议是单工位,滑动窗口是多工位并行。
关键参数:窗口大小 W 决定了吞吐量上限。理想情况下,吞吐量 = W × 包大小 / RTT。W 至少需要 ≥ 带宽 × RTT / 包大小,才能打满带宽。
三种滑动窗口变体
| 协议 | 接收方行为 | 重传策略 | 适用场景 |
|---|---|---|---|
| GBN(后退 N 步) | 只按序接收,乱序包丢弃 | 超时后重传窗口内所有包 | 信道质量好,丢包率低 |
| SR(选择重传) | 缓存乱序包,按序交付 | 只重传丢失的包 | 丢包率高,需要高效恢复 |
| TCP 变体 | 累积确认 + 选择性确认 | 结合超时和快速重传 | 互联网通用 |
GBN 的坑
GBN 实现简单,但有个致命问题——一个包丢了,后面所有包都得重传。我在项目中测试过,当丢包率到 5% 时,GBN 的有效吞吐量只有 SR 的 60%。
举个例子:窗口大小 10,第 3 个包丢了。接收方收到第 4-10 个包,但因为不是按序的,全部丢弃。发送方超时后,从第 3 个包开始重传 8 个包。浪费啊!
SR 的代价
SR 只重传丢失的包,效率高,但代价是接收方需要缓存。窗口越大,缓存越大。另外,序号范围也要扩大——GBN 只需要 W+1 个序号,SR 需要 2W 个序号。
我建议:如果丢包率低于 1%,用 GBN 就够了,实现简单,CPU 开销小。丢包率高于 1%,或者链路延迟大,果断上 SR。TCP 的 SACK 选项就是 SR 思想的体现。
核心逻辑图:ARQ 协议演进
下面这张图展示了从停等到滑动窗口的演进路径,以及 GBN 和 SR 的核心差异。
实践中的选择建议
说了这么多理论,落地时到底怎么选?我总结了几条经验:
- RTT 小于 10ms,丢包率低于 0.1%:停等协议就够了,实现最简单,CPU 开销最小
- RTT 在 10-100ms,丢包率低于 1%:GBN 是性价比之选,代码量少,性能可接受
- RTT 大于 100ms,或丢包率高于 1%:必须上 SR,否则带宽利用率惨不忍睹
- 互联网环境:TCP 的 SACK + 超时重传是经过实战检验的组合,别自己造轮子
我曾经犯过的错:在一个物联网项目里,设备通过 3G 网络通信,RTT 经常跳到 2 秒。我用了 GBN,窗口大小 16。结果一个包丢了,重传 16 个包,再丢再重传,整个链路直接死锁。后来换成 SR,问题解决。
ARQ 是丢包恢复的基石。停等协议让你理解基本原理,滑动窗口让你看到性能优化的空间。下一层我们会深入定时器设计和重传策略的细节——嗯,那些才是真正决定协议鲁棒性的地方。