重传机制全景:RTO、FR、SACK、DSACK、TLP
TCP 的重传机制,说白了就是网络出问题时的「后悔药」。数据包丢了怎么办?发出去没回应怎么办?TCP 靠一套组合拳来兜底。我刚开始调优时,总觉得重传就是超时重传那么简单,后来被线上问题狠狠教育了一顿——嗯,今天咱们把这五种机制一次讲透。
1. 超时重传(RTO)—— 最后的保底
RTO 是最原始、最保守的重传方式。发送方发出一个数据段后,启动一个定时器。如果定时器到期还没收到 ACK,就认为这个段丢了,直接重传。
RTO 的值不是固定的。TCP 会动态计算 RTT(往返时间),然后根据 RTT 的波动来调整 RTO。公式大致是:
RTO = SRTT + max(G, 4 × RTTVAR)
其中 SRTT 是平滑后的 RTT,RTTVAR 是 RTT 的偏差,G 是时钟粒度。说白了,RTO 要比当前 RTT 大一些,留出余量。
RTO 的缺点很明显:等待时间太长。尤其是在移动网络或跨洲链路中,RTT 本身就不小,再等一个 RTO,延迟直接翻倍。所以 TCP 又引入了更快的检测手段。
2. 快速重传(FR)—— 靠重复 ACK 触发
快速重传的思路很简单:接收方收到乱序包时,会重复发送期望的序列号。发送方收到 3 个重复的 ACK 后,不等超时,直接重传。
举个例子:
- 发送方发了 Seq=1, 2, 3, 4, 5
- 接收方收到了 1, 3, 4, 5,但 2 丢了
- 接收方会连续发 ACK=2(期望收到 2)
- 发送方收到 3 次 ACK=2 后,立即重传 Seq=2
快速重传比 RTO 快得多。它不需要等定时器,靠 ACK 的「重复信号」就能判断丢包。我在项目中见过不少场景,快速重传把恢复时间从秒级降到了毫秒级。
3. 选择性确认(SACK)—— 精准定位丢失
快速重传有个问题:它只知道某个包丢了,但不知道后面哪些包也丢了。比如丢了 Seq=2,但 Seq=3,4,5 都到了。快速重传只会重传 Seq=2,然后等 ACK 推进。如果后面还有丢包,还得再等下一轮。
SACK 解决了这个问题。接收方在 ACK 里带上 SACK 选项,告诉发送方:「我收到了哪些区间,哪些还没收到」。发送方一看就明白了,可以一次性重传所有丢失的段。
SACK 的格式大致如下:
TCP 选项:SACK
第一个块:左边界 = 1000,右边界 = 2000
第二个块:左边界 = 3000,右边界 = 4000
这意味着接收方收到了 1000-2000 和 3000-4000,中间 2000-3000 丢了。发送方直接重传这个区间就行。
4. 重复 SACK(DSACK)—— 揪出重复传输
DSACK 是 SACK 的增强版。它告诉发送方:「你发的某个包,我收到了不止一次」。说白了,就是报告重复传输。
DSACK 有什么用?两个场景:
- 检测虚假重传:如果发送方误判了丢包,重传了一个其实已经到达的段,DSACK 能暴露这个问题。
- 帮助调整 RTO:如果 RTO 设置得太激进,导致频繁虚假重传,DSACK 的数据可以用来校准 RTO 参数。
我记得有一次调优,发现重传率很高,但实际丢包率很低。打开 DSACK 一看,原来是中间设备乱序导致发送方误判。调整了接收窗口和乱序阈值后,问题就解决了。
5. 尾部丢失探测(TLP)—— 提前发现尾部丢包
TLP 是相对较新的机制,主要解决一个特殊场景:数据流快结束时,尾部包丢了怎么办?
举个例子:你发了 100 个包,最后一个包丢了。接收方不会发重复 ACK(因为没有后续包触发),只能等 RTO 超时。这个等待时间可能很长。
TLP 的做法是:在发送完一批数据后,如果在一定时间内没有收到任何 ACK,就主动发一个「探测包」。这个探测包可以是新的数据,也可以是重传的最后一个包。如果探测包触发了 ACK,发送方就知道网络还活着,继续等待;如果没触发,就启动 RTO。
TLP 的典型流程:
- 发送完数据后,启动 TLP 定时器(通常比 RTO 短)
- 定时器到期,发送一个探测包
- 收到 ACK → 继续正常传输
- 没收到 ACK → 进入 RTO 重传
五种机制的关系图
下面这张图展示了五种重传机制的触发条件和协作关系:
五种机制的对比
| 机制 | 触发条件 | 恢复速度 | 适用场景 |
|---|---|---|---|
| RTO | 定时器到期 | 慢(秒级) | 所有丢包场景(最后保底) |
| FR | 3次重复ACK | 快(毫秒级) | 单个丢包,后续有数据到达 |
| SACK | 与FR配合使用 | 快 | 多个丢包,需要精准恢复 |
| DSACK | 收到重复段 | 诊断用途 | 检测虚假重传、调整参数 |
| TLP | 尾部无ACK | 中等(比RTO快) | 短连接、尾部丢包 |
ss -i 查看当前连接的 SACK 和 TLP 状态。如果发现 SACK 没开启,检查 net.ipv4.tcp_sack 参数。TLP 在 3.x 以上内核默认开启,老内核需要手动配置。
五种机制各有分工。RTO 是兜底,FR 是快速响应,SACK 是精准定位,DSACK 是诊断工具,TLP 是尾部补漏。你想想看,如果没有这套组合拳,TCP 在网络差的时候得有多狼狈?