
当网络发生丢包时,TCP的重传机制是确保数据可靠性的核心。然而,一个关键问题随之而来:重传定时器(RTO)超时后,到底应该重传哪些数据? 是只重传最早的那个丢失的报文段,还是应该把发送窗口中已发送但未确认的数据一股脑都重传一次?
选择不同的重传策略,会直接影响连接的性能和带宽利用率。本文将深入探讨几种主流的TCP重传策略,分析其优劣与适用场景。
1. 传统重传方法(仅重传最早未确认段)
这是最直观、最经典的策略。当RTO超时时,发送方认为序列号最小的那个未确认报文段(即最早发送的)丢失了,于是仅重传这一个报文段。
- 优点: 保守、节约带宽。不会重传可能已经到达对端但ACK丢失的数据。
- 缺点: 恢复效率低。如果连续多个包丢失(如在一个网络拥塞周期内),采用这种方式需要一个RTT才能恢复一个丢失包, recovery time 会很长,严重影响吞吐量。这被称为
Go-Back-N 行为在重传上的体现。
2. 重传所有未确认数据
这是一种更为激进和悲观的策略。一旦RTO超时,发送方认为网络状况可能极度恶劣,因此将发送窗口中所有已发送但未收到ACK的数据全部重传。
- 优点: 恢复速度快。在多个包丢失的情况下,可以一次性“填坑”,快速推进发送窗口。
- 缺点: 浪费带宽。会重传大量可能已经成功到达接收端的数据(比如只是对应的ACK丢失或延迟了)。这种不必要的重传会加剧网络拥塞,形成恶性循环。
3. 基于SACK的选择性重传
为了克服上述两种方法的缺点,现代TCP协议普遍支持 选择性确认(SACK) 选项。接收方可以告诉发送方自己具体收到了哪些不连续的数据块。当发生RTO超时时,发送方可以结合SACK信息,智能地只重传那些被推断为真正丢失的报文段。
其核心算法(如 TCP FACK 或 Linux内核中的相关逻辑)通常是:
- 确定发送窗口中最高的已被SACK确认的序列号。
- 重传那些序列号低于这个“最高SACK点”、且未被任何SACK块覆盖的报文段。
- 优点: 高效且精准。极大地减少了不必要的重传,加快了从多包丢失中的恢复速度。
- 缺点: 依赖于接收端对SACK选项的支持和正确实现。在复杂的网络环境下,推断“哪些包真丢了”的算法本身也可能出错。
4. 超时后重置cwnd与慢启动
无论采用上述哪种重传策略,一个至关重要的共同点是:RTO超时被视为强烈的拥塞信号。因此,几乎所有TCP实现(如Reno, CUBIC)在发生超时重传时,都会执行以下操作:
- 大幅度减小拥塞窗口(cwnd): 通常直接重置为
1 MSS(一个最大报文段大小)。
- 进入慢启动阶段: 重新开始指数增长,探测可用带宽。
- 将慢启动阈值(ssthresh)设置为当前cwnd的一半。
这与快速重传/快速恢复(由重复ACK触发)形成鲜明对比。快速恢复通常只将cwnd减半,然后进入拥塞避免阶段,性能影响相对较小。因此,优化网络应用和系统调优的一个关键目标就是尽量避免RTO超时,更多地触发快速重传。
关键场景与策略选择
在实际网络编程和内核调优中,策略的选择和调优至关重要:
- 高延迟、低丢包网络(如卫星链路): 倾向于使用更保守的策略(如传统方法或精心调参的SACK),避免误判和带宽浪费。
- 高吞吐、高丢包网络(如无线网络): 需要更激进的恢复策略。基于SACK的选择性重传是必须的。有时甚至需要配合类似“尾部丢包探测”的机制,因为窗口末尾的包丢失可能无法产生足够的Dup ACK来触发快速重传。
- 内核参数调优: 在Linux中,可以通过
sysctl 参数(如 net.ipv4.tcp_sack, net.ipv4.tcp_fack)来启用或调整相关行为。例如,禁用SACK会让TCP回退到更原始的重传模式。
总结
“TCP重传时该重传哪些数据”并非一个简单的问题。从最初的仅重传最早未确认段,到悲观的重传所有未确认数据,再到如今基于SACK的智能选择性重传,其演进历程体现了在可靠性、效率和资源消耗之间持续的权衡与优化。
理解这些策略的差异,有助于开发者在进行网络协议相关的性能分析和调优时,能够更准确地定位问题根源。例如,若发现Wireshark抓包中在超时后出现大量重复数据,可能就是激进的“重传所有”策略在起作用;而恢复缓慢则可能与保守策略或SACK信息利用不足有关。掌握这些底层机制,是构建高性能、高可靠网络应用的基础。
|