概述
TCP 是一种可靠的传输协议,其核心目标之一就是确保数据能够准确无误地到达目的地。重传机制是实现这一目标的关键技术。当数据丢失或损坏时,TCP 通过重传机制来恢复数据,保证传输的可靠性。
1. 超时重传 (RTO)
1.1 基本原理
超时重传是 TCP 最基本的重传机制。其工作原理如下:
-
发送方发送数据段后,启动一个定时器(称为重传定时器 RTO)
-
如果在定时器超时前收到了对端的确认(ACK),则取消定时器
-
如果定时器超时仍未收到确认,则认为数据段丢失,重新发送该数据段
1.2 RTO 的计算
RTO(Retransmission TimeOut)不是固定值,而是动态计算的。TCP 使用 Karn 算法和 Jacobson/Karels 算法来动态调整 RTO:
```
SRTT = (1 - α) * SRTT + α * RTT_sample
RTTVAR = (1 - β) * RTTVAR + β * |RTT_sample - SRTT|
RTO = SRTT + max(G, 4 * RTTVAR)
```
其中:
-
SRTT:平滑往返时间(Smoothed RTT)
-
RTTVAR:RTT 变化量(RTT Variance)
-
α:平滑因子,通常为 1/8
-
β:变化量因子,通常为 1/4
-
G:时钟粒度,通常为 100ms
1.3 实际案例
**场景:** 客户端向服务器发送文件
```
时间线:
0ms -> 发送 Segment 1 (Seq=100, Size=1000)
100ms -> 收到 ACK (Ack=1100),RTO = 200ms
200ms -> 发送 Segment 2 (Seq=1100, Size=1000)
400ms -> 定时器超时!未收到 ACK
400ms -> 重传 Segment 2
500ms -> 收到 ACK (Ack=2100)
```
**问题分析:**
-
第一次 RTT = 100ms,计算出 RTO = 200ms
-
Segment 2 在 200ms 发送,但可能由于网络拥塞丢失
-
400ms 时定时器超时,触发重传
-
重传后很快收到确认,说明数据确实丢失了
1.4 指数退避
每次超时重传后,RTO 会加倍(指数退避):
```
第1次超时:RTO = 200ms
第2次超时:RTO = 400ms
第3次超时:RTO = 800ms
第4次超时:RTO = 1600ms
...
```
这是为了避免在网络拥塞时过度发送数据,加剧网络负担。
2. 快速重传 (Fast Retransmit)
2.1 基本原理
快速重传是对超时重传的改进。它不依赖定时器超时,而是通过检测重复 ACK 来快速判断数据丢失。
**核心思想:** 如果收到 3 个重复的 ACK,说明后续数据已经到达,当前数据段很可能丢失了。
2.2 工作流程
```
发送方:
-
发送 Segment 1 (Seq=1)
-
发送 Segment 2 (Seq=101)
-
发送 Segment 3 (Seq=201)
接收方(Segment 2 丢失):
-
收到 Segment 1,回复 ACK=101
-
收到 Segment 3,期望 Seq=101,发现乱序
-
回复重复 ACK=101(告诉发送方期望收到 Seq=101)
-
继续收到后续数据,继续回复重复 ACK=101
发送方:
-
收到第1个重复 ACK=101
-
收到第2个重复 ACK=101
-
收到第3个重复 ACK=101 → 立即重传 Segment 2,无需等待超时
```
2.3 实际案例
**场景:** 视频流传输
```
发送方发送顺序:
Packet A (Seq=1) → Packet B (Seq=1001) → Packet C (Seq=2001) → Packet D (Seq=3001)
网络情况:Packet B 在传输中丢失
接收方:
-
收到 Packet A → ACK(1001)
-
收到 Packet C → 期望 Seq=1001,实际 Seq=2001,发送重复 ACK(1001)
-
收到 Packet D → 期望 Seq=1001,实际 Seq=3001,发送重复 ACK(1001)
-
收到 Packet E → 期望 Seq=1001,实际 Seq=4001,发送重复 ACK(1001)
发送方:
-
连续收到 3 个 ACK(1001)
-
立即重传 Packet B,无需等待 RTO(假设 RTO=200ms)
-
节省了大约 200ms 的等待时间
```
2.4 快速重传的优势
| 对比项 | 超时重传 | 快速重传 |
|-------|---------|---------|
| 检测延迟 | RTO 时间(通常 200ms+) | 3 个重复 ACK(通常 < 100ms) |
| 适用场景 | 数据完全丢失 | 数据乱序或部分丢失 |
| 响应速度 | 较慢 | 快速 |
3. SACK(选择性确认)
3.1 问题背景
传统的累积确认(Cumulative ACK)存在一个问题:当多个数据段丢失时,发送方只能知道第一个未确认的数据段,无法知道后续哪些数据已经到达。
**例子:**
```
发送方发送:Seg1, Seg2, Seg3, Seg4, Seg5
接收方收到:Seg1, Seg3, Seg4, Seg5(Seg2 丢失)
传统 ACK:只能回复 ACK=Seg2的Seq(表示期望收到Seg2)
发送方不知道:Seg3, Seg4, Seg5 是否到达
结果:发送方可能会重传 Seg2, Seg3, Seg4, Seg5(冗余重传)
```
3.2 SACK 的解决方案
SACK(Selective Acknowledgment)允许接收方显式告知发送方哪些数据段已经成功接收,即使它们是不连续的。
**TCP 选项格式:**
```
Kind=5, Length=变量
+--------+--------+--------+--------+
| Kind=5 | Length | LeftEdge| RightEdge| ...
+--------+--------+--------+--------+
```
3.3 实际案例
**场景:** 文件传输,Seg2 和 Seg4 丢失
```
发送方发送顺序:
Seg1 (1-1000), Seg2 (1001-2000), Seg3 (2001-3000), Seg4 (3001-4000), Seg5 (4001-5000)
接收方收到:Seg1, Seg3, Seg5
Seg2 和 Seg4 丢失
传统 ACK:只能回复 ACK=1001
发送方行为:重传 Seg2, Seg3, Seg4, Seg5(冗余)
SACK 方式:
接收方回复:ACK=1001, SACK=[(2001-3000), (4001-5000)]
表示:
-
期望收到 Seq=1001(Seg2)
-
已收到 Seg3(2001-3000)
-
已收到 Seg5(4001-5000)
发送方行为:只重传 Seg2 和 Seg4
```
3.4 SACK 的优势
```
传统方式:
发送: [Seg1] [Seg2] [Seg3] [Seg4] [Seg5]
收到: [Seg1] [---] [Seg3] [---] [Seg5]
ACK: ACK=1001
重传: [Seg2] [Seg3] [Seg4] [Seg5] ← 冗余!
SACK 方式:
发送: [Seg1] [Seg2] [Seg3] [Seg4] [Seg5]
收到: [Seg1] [---] [Seg3] [---] [Seg5]
ACK: ACK=1001, SACK=[Seg3, Seg5]
重传: [Seg2] [Seg4] ← 精准!
```
4. D-SACK(重复 SACK)
4.1 问题背景
SACK 可以告诉发送方哪些数据已收到,但无法区分:
-
数据段是第一次到达
-
数据段是重传后到达(可能因为原始数据段延迟到达)
**场景:** 网络延迟导致重复数据
```
发送方:
-
发送 Seg1,超时重传 Seg1
-
原始 Seg1 和重传 Seg1 都到达接收方
-
接收方收到重复数据,但发送方不知道
问题:发送方可能误以为数据仍未到达,继续重传
```
4.2 D-SACK 的解决方案
D-SACK(Duplicate SACK)扩展了 SACK,允许接收方标记重复收到的数据段。
**D-SACK 标志:**
- SACK 块的 Left Edge = Right Edge - 1,表示这是一个重复确认块
4.3 实际案例
**场景:** 网络延迟导致重复
```
时间线:
T0: 发送方发送 Seg1 (Seq=1-1000)
T100: RTO 超时,重传 Seg1
T150: 原始 Seg1 到达接收方
T160: 重传 Seg1 到达接收方
接收方处理:
T150: 收到 Seg1 → ACK=1001
T160: 收到重复 Seg1 → ACK=1001, SACK=[(1-1000)] ← D-SACK
发送方处理:
收到 ACK=1001, SACK=[(1-1000)]
发现 SACK 块表示已经收到过 Seq=1-1000
推断:原始 Seg1 延迟到达,不是丢失
行为:停止重传,不调整 RTO
```
4.4 D-SACK 的应用场景
```
场景1:重复数据检测
发送: Seg1 → 超时重传 Seg1 → 两者都到达
D-SACK: 告诉发送方收到重复,避免继续重传
场景2:网络拥塞诊断
如果发送方频繁收到 D-SACK,可能说明:
-
网络存在严重延迟
-
路由抖动(数据走不同路径)
-
可以作为拥塞控制的参考信号
场景3:RTO 调整优化
收到 D-SACK 后,发送方知道不是真正丢失
可以避免不必要的 RTO 指数退避
```
5. 乱序重传
5.1 乱序的原因
网络中数据乱序到达的常见原因:
-
**多路径路由**:数据通过不同路径到达
-
**队列调度**:路由器队列调度策略导致顺序变化
-
**链路拥塞**:部分数据被延迟
5.2 乱序的处理
**接收方的乱序处理策略:**
```
接收缓冲区: [Seg1][---][Seg3][Seg4][---][Seg6]
↑ ↑
已确认 期望位置
处理方式:
-
保存乱序数据到接收缓冲区
-
发送重复 ACK 告知发送方期望的 Seq
-
等待缺失的数据段到达
-
连续数据到达后,一次性确认
```
**发送方的乱序处理策略:**
```
收到重复 ACK 时:
-
1-2 个重复 ACK:可能只是轻微乱序,等待
-
3 个重复 ACK:触发快速重传
-
结合 SACK:精准判断哪些需要重传
```
5.3 实际案例
**场景:** 多路径传输
```
网络拓扑:
发送方 ──路径A── 路由器1 ──路径C── 接收方
└─路径B── 路由器2 ──路径D──┘
发送顺序:Seg1, Seg2, Seg3
传输情况:
Seg1 → 路径A→C(延迟 50ms)
Seg2 → 路径B→D(延迟 150ms)
Seg3 → 路径A→C(延迟 60ms)
接收顺序:
T50: Seg1 到达 → ACK=Seg2_Seq
T60: Seg3 到达(乱序)→ ACK=Seg2_Seq(重复ACK)
T150: Seg2 到达 → ACK=Seg4_Seq(累积确认)
```
6. 重传机制对比总结
| 机制 | 触发条件 | 优势 | 劣势 | 典型场景 |
|-----|---------|------|------|---------|
| 超时重传 | RTO 定时器超时 | 简单可靠 | 延迟高(RTO时间) | 数据完全丢失 |
| 快速重传 | 3 个重复 ACK | 响应快 | 需要收到后续数据 | 数据乱序或部分丢失 |
| SACK | 支持 SACK 选项 | 精准重传 | 需要双方支持 | 多段丢失场景 |
| D-SACK | 收到重复数据 | 避免重复重传 | 协议复杂度增加 | 网络延迟抖动 |
7. 实际应用中的配置
7.1 Linux 系统参数
```bash
查看当前 TCP 重传相关参数
sysctl -a | grep tcp | grep -E "(rto|retrans|sack)"
关键参数说明:
tcp_sack = 1 # 启用 SACK
tcp_dsack = 1 # 启用 D-SACK
tcp_fastopen = 3 # 启用快速打开
tcp_retries2 = 15 # 最大重传次数
tcp_syn_retries = 6 # SYN 包重传次数
```
7.2 常见调优场景
```
场景1:低延迟要求(如高频交易)
-
降低初始 RTO(但可能增加重传次数)
-
启用 TCP Fast Open
-
使用 BBR 拥塞控制
场景2:长距离传输(如跨洲际)
-
增大初始 RTO
-
启用 SACK/D-SACK
-
调整 TCP 窗口大小
场景3:无线网络(如 5G)
-
增加最大重传次数
-
启用 D-SACK(处理链路抖动)
-
使用 TCP HyStart++
```
8. 总结
TCP 的重传机制是一个复杂而精妙的系统:
-
**超时重传**提供了最基本的保障,是最后的兜底方案
-
**快速重传**通过检测重复 ACK 实现快速响应
-
**SACK** 解决了累积确认的局限性,实现精准重传
-
**D-SACK** 解决了重复数据的问题,优化了 RTO 调整
-
**乱序处理**确保了在复杂网络环境下的可靠性
这些机制相互配合,使得 TCP 能够在各种网络条件下提供可靠的数据传输,是互联网稳定运行的基石。