这是《计算机网络 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 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
超时后:
- 重传最早未确认数据;
- 通常指数退避 RTO;
- 拥塞窗口收缩;
- 吞吐下降。
高 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)
发送方窗口移动:
已发送已确认 | 已发送未确认 | 可发送未发送 | 不可发送
<-------- 发送窗口 -------->
接收方通过窗口通告控制对端发送量:
- 应用读取慢,接收缓冲堆积;
- 接收窗口缩小;
- 窗口为 0 时发送方暂停;
- 接收方窗口打开后发送窗口更新;
- 持续零窗口会触发探测和超时。
查看:
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 零窗口与粘包
零窗口
现象:
- 发送暂停;
- 接收窗口为 0;
- 接收应用处理慢;
- 连接长期空闲但未断开。
排查 ss -i 和应用读取逻辑。
TCP 粘包与半包
TCP 是字节流,没有消息边界:
write("A1")
write("B2")
对端可能一次读到 "A1B2"
应用协议需要定义边界:
- 固定长度;
- 分隔符;
- 长度前缀;
- HTTP 头部长度加 body;
- RPC 协议帧。
示例长度前缀:
| 4 字节长度 | payload |
9.9 可靠性误区
- TCP 可靠不等于请求一定成功;
- 重传可能带来长尾延迟;
- 连接存活不等于服务健康;
- 有序性可能造成队头阻塞;
- 发送成功只代表进入内核发送队列;
- 应用仍需要幂等、超时和业务确认;
- 大量重传可能表现为应用超时。
本章小结
TCP 通过序列号和确认号标识字节流,通过超时重传、快速重传和 SACK 恢复丢失,通过滑动窗口和接收窗口控制发送节奏。窗口小于带宽时延积会限制吞吐,Nagle 与 Delayed ACK可能影响小请求延迟,字节流特性要求应用自行划分消息边界。
思考题
- 累积确认和 SACK 有什么区别?
- 为什么三次重复 ACK 可以触发快速重传?
- 计算 1Gbps、30ms RTT 链路的 BDP。
- TCP 为什么会出现粘包和半包?
- 为什么 TCP 可靠传输仍需要应用层重试和幂等?