TCP 可靠传输机制
序列号、确认应答、超时重传、滑动窗口 — TCP 如何在不可靠的网络上保证可靠传输?
1. 可靠的基石:序列号与确认
RFC 9293 明确指出:TCP 连接上发送的每个字节都有一个序列号。32 位序列号空间从 0 到 2^32-1(约 4.3 GB),循环使用。
1.1 序列号(Sequence Number)
TCP 为每个发送的字节编号。段头部的序列号字段是该段中第一个数据字节的序列号。
段中数据:[ 字节A | 字节B | 字节C | 字节D ]
↑
SEQ = 序列号(A)
注意:SYN 和 FIN 标志也消耗一个序列号。携带 SYN 的段,SEQ 就是 ISN,第一个数据字节的序列号为 ISN+1。
1.2 确认号(Acknowledgment Number)
确认号表示"我期望收到的下一个字节的序列号"。TCP 使用累积确认——ACK=X 意味着 X-1 及之前的所有字节都已收到。
发送方 接收方
|── seq=100, data(200字节) ──→| 收到 100~299
| |
|←── ack=300 ────────────────| "我要第 300 号"
2. 超时重传(RTO)
2.1 RTO 的计算
TCP 根据测量的往返时间(RTT)动态计算重传超时(RTO)。RFC 6298 规定的经典算法:
SRTT = (1-α) × SRTT + α × RTT_measured (α = 1/8)
RTTVAR = (1-β) × RTTVAR + β × |SRTT - RTT_measured| (β = 1/4)
RTO = SRTT + max(G, 4 × RTTVAR) (G = 时钟粒度)
2.2 Karn 算法
问题:重传后收到的 ACK,无法区分是对原始段还是重传段的确认。如果误用会导致 RTT 估算严重失真。
Karn 算法:重传的段不用于更新 RTT 估计。当收到重传段的 ACK 时,RTO 翻倍(指数退避),直到收到非重传段的 ACK。
2.3 重传策略
- 超时未收到 ACK → 重传最早的未确认段 → RTO 翻倍
- 连续重传多次(通常 5-15 次)仍未成功 → 认为连接失效,发送 RST 关闭
3. 滑动窗口(Sliding Window)
滑动窗口是 TCP 实现流量控制的核心机制。
3.1 窗口的概念
接收方通过 TCP 头部的窗口大小字段(16 位,最大 65535 字节,可用窗口缩放选项扩展)告知发送方:
"我还能收多少字节,请别发超了"
发送方窗口:
├──── 已发送已确认 ────┼── 已发送未确认 ──┼── 可发送未发送 ──┼── 不可发送 ────┤
↑ SND.UNA ↑ SND.NXT ↑ SND.UNA+SND.WND
←─── SND.WND ──────→
3.2 窗口滑动
随着 ACK 到达,窗口向右滑动:
ACK 前: [已确认...|未确认...|可发送...|不可发送...]
ACK 后: [........已确认........|未确认...|可发送...|不可发送...]
← 窗口右移 →
3.3 零窗口处理
当接收方窗口为 0 时,发送方停止发送并启动持续定时器(Persist Timer),周期性发送窗口探测段(1 字节数据),检查窗口是否恢复。
窗口综合症(Silly Window Syndrome):接收方每次只释放很小窗口(如 1 字节),发送方每次只发很少数据。双方均使用 Clark 算法避免:
- 接收方:窗口足够大(至少 1 MSS 或缓冲区的一半)才通告
- 发送方:积累足够数据(至少 1 MSS 或窗口的一半)才发送
4. 流量控制 vs 拥塞控制
| 维度 | 流量控制 | 拥塞控制 |
|---|---|---|
| 目的 | 防止发送方淹没接收方 | 防止发送方淹没网络 |
| 关注对象 | 接收方的处理能力 | 网络的承载能力 |
| 核心机制 | 滑动窗口(rwnd) | 拥塞窗口(cwnd) |
| 信息来源 | 接收方通告窗口大小 | 丢包/延迟等网络信号 |
| 发送上限 | min(cwnd, rwnd) | — |
发送方实际可发送量 = min(拥塞窗口 cwnd, 接收方通告窗口 rwnd)
5. 选择性确认(SACK)
传统 TCP 只支持累积确认——ACK=X 表示 X 之前的所有字节都收到了。如果一个段丢失,即使后面的段都收到了,ACK 还是指向丢失处,发送方需要重传丢失段及之后所有段。
SACK(Selective Acknowledgment)[RFC 2018] 允许接收方通过 TCP 选项告知发送方:
"我收到了字节 1000-1999 和 3000-3999,但 2000-2999 丢了"
发送方可以只重传丢失的 2000-2999,不需要重传 3000-3999。这在长肥管道(LFN)上显著提升性能。
6. 小结
TCP 的可靠传输由多个机制协同实现:
- 序列号:每个字节有编号,保证有序
- 确认应答:累积确认,知道哪些数据已到达
- 超时重传:未确认的数据有人兜底重发
- 滑动窗口:接收方说"只能收这么多",发送方不超发
- SACK:精准定位丢失段,避免不必要的重传
这些机制让 TCP 在不可靠的 IP 网络上实现了"万无一失"。
本篇要点
- TCP 可靠传输的六大机制:序列号、确认应答(累积确认)、超时重传、校验和、滑动窗口(流量控制)、拥塞控制
- 累积确认:ACK=N 表示 N-1 及之前的所有字节都已收到;优点是实现简单,缺点是一个段丢失会阻塞后续确认
- RTO 动态计算:RTO = SRTT + 4×RTTVAR,Karn 算法规定重传段不用于 RTT 估算(避免二义性)
- 滑动窗口实现流量控制:接收方通过窗口大小告知发送方能收多少,窗口为 0 时发送方启动持续定时器探测
- SACK(选择性确认)让发送方只重传真正丢失的段,避免累积确认导致的不必要重传
- 发送方实际可发送量 = min(拥塞窗口 cwnd, 接收方通告窗口 rwnd)
- 判断重传的两种方式:超时重传(RTO 到期)和快速重传(连续 3 个重复 ACK)