Computer NetworkNotes

第 09 章:可靠传输与滑动窗口

zjc 于 2026-01-09 发布

这是《计算机网络 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 TCP 的可靠性建立在序列号、确认应答、超时重传、快速重传、滑动窗口和流量控制之上。它保证字节流有序、不丢、不重复,但不保证时延,也不保证应用语义成功。

9.1 序列号与确认号

Seq:本报文段数据第一个字节在字节流中的编号
ACK:期望下一个收到的字节编号

示例:

Sender -> Seq=1000, 100 bytes
Receiver -> ACK=1100

确认号表示 1100 之前的字节已收到。

累积确认:

收到 100-199、200-299、300-399
ACK=400 表示 400 前全部收到

如果只收到 100-199 和 300-399,累积 ACK 只能到 200;SACK 可以补充说明 300-399 也已收到。

9.2 超时重传

TCP 根据 RTT 动态计算 RTO:

SRTT:平滑 RTT
RTTVAR:RTT 方差
RTO:重传超时

简化关系:

RTO 略大于 SRTT + 若干倍 RTTVAR

超时后:

  1. 重传最早未确认数据;
  2. 通常指数退避 RTO;
  3. 拥塞窗口收缩;
  4. 吞吐下降。

高 RTT 链路中,一次丢包的恢复成本远高于低 RTT 链路,因此跨地域系统要减少请求轮次。

9.3 快速重传

接收方每收到一个失序段,重复发送当前期望 ACK:

Sender -> Seq=100
Sender -> Seq=200
Sender -> Seq=300
Sender -> Seq=400

Receiver -> ACK=200
Receiver -> ACK=200
Receiver -> ACK=200
Receiver -> ACK=200

发送方收到 3 个重复 ACK 后,不必等超时,立即重传丢失段。

9.4 SACK

SACK 允许接收方告知已收到的不连续区间:

ACK = 200
SACK = 300-400
SACK = 500-600

发送方可以更准确判断只重传缺失部分。

Linux 通常默认启用:

sysctl net.ipv4.tcp_sack

9.5 滑动窗口

发送窗口由接收窗口和拥塞窗口共同限制:

effective window = min(rwnd, cwnd)

发送方窗口移动:

已发送已确认 | 已发送未确认 | 可发送未发送 | 不可发送
              <-------- 发送窗口 -------->

接收方通过窗口通告控制对端发送量:

  1. 应用读取慢,接收缓冲堆积;
  2. 接收窗口缩小;
  3. 窗口为 0 时发送方暂停;
  4. 接收方窗口打开后发送窗口更新;
  5. 持续零窗口会触发探测和超时。

查看:

ss -i

重点关注:

cwnd
rwnd
rtt
retrans
unacked

9.6 窗口缩放

TCP 头部窗口字段最大 65535 字节。Window Scale 选项允许扩大窗口:

实际窗口 = 头部窗口值 << shift

高带宽长延迟网络需要大窗口:

BDP = bandwidth × RTT

示例:

带宽 100 Mbps
RTT 50 ms
BDP = 100,000,000 / 8 × 0.05 = 625,000 字节

若窗口小于 BDP,即使带宽充足,吞吐也上不去。

9.7 Nagle 与 Delayed ACK

Nagle 减少小包:

只有所有数据都被确认,或积攒到 MSS,才发送新的小段

Delayed ACK 延迟发送 ACK,希望合并响应。

两者组合可能带来延迟:

发送方等待 ACK 或攒包
接收方延迟 ACK
-> 小请求延迟上升

低延迟交互系统通常关闭 Nagle:

TCP_NODELAY

查看:

sysctl net.ipv4.tcp_low_latency

注意:关闭 Nagle 会增加小包数量,应结合业务形态评估。

9.8 零窗口与粘包

零窗口

现象:

  1. 发送暂停;
  2. 接收窗口为 0;
  3. 接收应用处理慢;
  4. 连接长期空闲但未断开。

排查 ss -i 和应用读取逻辑。

TCP 粘包与半包

TCP 是字节流,没有消息边界:

write("A1")
write("B2")
对端可能一次读到 "A1B2"

应用协议需要定义边界:

  1. 固定长度;
  2. 分隔符;
  3. 长度前缀;
  4. HTTP 头部长度加 body;
  5. RPC 协议帧。

示例长度前缀:

| 4 字节长度 | payload |

9.9 可靠性误区

  1. TCP 可靠不等于请求一定成功;
  2. 重传可能带来长尾延迟;
  3. 连接存活不等于服务健康;
  4. 有序性可能造成队头阻塞;
  5. 发送成功只代表进入内核发送队列;
  6. 应用仍需要幂等、超时和业务确认;
  7. 大量重传可能表现为应用超时。

本章小结

TCP 通过序列号和确认号标识字节流,通过超时重传、快速重传和 SACK 恢复丢失,通过滑动窗口和接收窗口控制发送节奏。窗口小于带宽时延积会限制吞吐,Nagle 与 Delayed ACK可能影响小请求延迟,字节流特性要求应用自行划分消息边界。

思考题

  1. 累积确认和 SACK 有什么区别?
  2. 为什么三次重复 ACK 可以触发快速重传?
  3. 计算 1Gbps、30ms RTT 链路的 BDP。
  4. TCP 为什么会出现粘包和半包?
  5. 为什么 TCP 可靠传输仍需要应用层重试和幂等?