4. 快速重传:重复ACK触发机制,与超时重传的对比,实际抓包分析

说到TCP重传,很多人第一反应就是超时重传。但说实话,等超时再重传,效率太低了。你想想看,一个数据包丢了,发送方要傻等一个RTO(重传超时时间)才能发现,这期间网络可能早就空闲了。

快速重传就是为了解决这个问题而生的。它不需要等超时,而是靠接收方发回的重复ACK来触发。我个人觉得,这是TCP协议里最巧妙的设计之一。

4.1 重复ACK是怎么产生的

先看一个典型场景。发送方发了三个包:Seq=1、Seq=2、Seq=3。接收方只收到了Seq=1和Seq=3,Seq=2丢了。

接收方的行为是这样的:

  • 收到Seq=1,正常回复ACK=2(期望下一个收到Seq=2)
  • 收到Seq=3,但Seq=2还没到,回复ACK=2(重复ACK)
  • 后续再收到任何包,只要Seq=2没到,就一直回复ACK=2

这就是重复ACK的由来。说白了,接收方在说:「我一直在等你发Seq=2,别发后面的了,先把丢的那个补上!」

关键点:重复ACK不是乱发,它只重复确认「接收方期望收到的下一个序列号」。收到3个重复ACK(包括原始ACK共4个),发送方就认为发生了丢包。

4.2 快速重传 vs 超时重传

这两个机制的区别,我整理了一张对比表。我在项目里经常拿这个表给团队新人讲,效果不错。

对比维度 快速重传 超时重传
触发条件 收到3个重复ACK RTO计时器到期
检测速度 快,约1个RTT内 慢,至少1个RTO(通常200ms+)
拥塞控制 触发快速恢复(cwnd减半) 触发慢启动(cwnd=1)
适用场景 单个包丢失,后续包成功到达 严重拥塞或链路中断
对吞吐的影响 较小,能快速恢复 较大,可能造成空闲期

这里有个坑,我踩过。有一次线上服务延迟飙升,我一看监控,超时重传次数暴涨。但抓包一看,其实只是个别包丢了,快速重传没触发。为什么?因为接收方收到的后续包太少,凑不够3个重复ACK。这种情况,快速重传就帮不上忙了。

注意:快速重传依赖后续包的到达来产生重复ACK。如果丢包后发送方停止发送,或者接收方缓存满了,重复ACK就不会出现。这时候只能靠超时重传来兜底。

4.3 实际抓包分析

光讲理论不过瘾,咱们直接看抓包。下面是我从一次真实故障中截取的片段。

# 抓包片段:快速重传过程
1   0.000000  192.168.1.1 → 192.168.1.2  TCP  Seq=1000, Len=1460
2   0.000100  192.168.1.2 → 192.168.1.1  TCP  ACK=2460
3   0.000200  192.168.1.1 → 192.168.1.2  TCP  Seq=2460, Len=1460  ← 这个包丢了
4   0.000300  192.168.1.1 → 192.168.1.2  TCP  Seq=3920, Len=1460
5   0.000400  192.168.1.2 → 192.168.1.1  TCP  ACK=2460  ← 第1个重复ACK
6   0.000500  192.168.1.1 → 192.168.1.2  TCP  Seq=5380, Len=1460
7   0.000600  192.168.1.2 → 192.168.1.1  TCP  ACK=2460  ← 第2个重复ACK
8   0.000700  192.168.1.1 → 192.168.1.2  TCP  Seq=6840, Len=1460
9   0.000800  192.168.1.2 → 192.168.1.1  TCP  ACK=2460  ← 第3个重复ACK
10  0.000900  192.168.1.1 → 192.168.1.2  TCP  Seq=2460, Len=1460  ← 快速重传!

看第3行,Seq=2460的包丢了。但发送方不知道,继续发后面的包。接收方收到Seq=3920、5380、6840后,连续回了3个ACK=2460。发送方在第10行立即重传了Seq=2460。整个过程只用了0.9毫秒,比等超时快太多了。

抓包技巧:用Wireshark过滤快速重传,可以直接用 tcp.analysis.fast_retransmission。这个过滤器会标出所有被判定为快速重传的包。我排查问题时,习惯先看这个过滤结果,再结合 tcp.analysis.retransmission 看超时重传,两个一对比,问题基本就清楚了。

4.4 快速重传的局限性

快速重传不是万能的。我遇到过一种情况:发送方发了10个包,只有第一个丢了,后面9个都到了。按理说能触发快速重传。但问题是,接收方的接收窗口满了,后面的包被丢弃了。结果重复ACK没发出来,最后还是超时重传。

另外,快速重传对乱序包也很敏感。如果网络本身就有乱序,接收方可能会误判为丢包,触发不必要的快速重传。嗯,这里要注意,乱序和丢包在接收方看来,表现是一样的——都是「期望的包没到」。区别在于,乱序的包稍后会到,而丢包的不会。

我曾经在一条高延迟链路上遇到过这个问题。链路本身有轻微的乱序,结果快速重传频繁触发,吞吐量反而下降了。后来我们调整了重复ACK阈值,从3改到5,才缓解了这个问题。

4.5 知识体系图

下面这张图总结了快速重传的核心逻辑和与超时重传的关系。我建议你把它记在脑子里,排查问题时能快速定位。

快速重传与超时重传对比流程图 TCP重传机制:快速重传 vs 超时重传 发送方发送数据包 接收方检查序列号 是否按序到达? 是 回复正常ACK 否 回复重复ACK 收到3个重复ACK? 是 快速重传 + 快速恢复 否 等待RTO超时重传 快速重传:收到3个重复ACK即触发,无需等待超时,能更快恢复传输 超时重传:RTO到期才触发,作为兜底机制,应对严重拥塞或链路中断

4.6 优化建议

最后,结合我的实战经验,给你几条优化建议:

  1. 监控重复ACK比例:如果重复ACK占比超过1%,说明网络丢包率偏高,需要排查链路质量。
  2. 调整重复ACK阈值:默认是3,但在高乱序网络中,可以适当调高到4或5。Linux下通过 sysctl net.ipv4.tcp_reordering 调整。
  3. 开启SACK:选择性确认能让接收方精确告诉发送方丢了哪些包,避免不必要的重传。我建议所有场景都开启。
  4. 注意接收窗口:如果接收窗口太小,后续包可能被丢弃,导致重复ACK无法产生。适当增大接收缓冲区能缓解这个问题。

快速重传和超时重传,一个快一个稳,配合起来才能保证TCP的可靠性。你在实际排查中,可以先用快速重传定位丢包,再结合超时重传判断拥塞程度。两者结合,问题定位会快很多。