计算机网络:TCP 是怎么保证可靠传输的?

#计算机网络#操作系统#面试八股

计算机网络:TCP 是怎么保证可靠传输的?

结论:TCP 把"尽力而为"的 IP 分组,包装成有序、无重复、可重传的字节流。 手段是五件套:序号与确认、超时重传、校验和、滑动窗口、拥塞控制——每一件都对应一种具体的失败模式,不是玄学。

1. 先分清失败模式,再谈机制

故障对应机制关键点
数据丢失超时重传 / 快速重传RTO 动态估算,收到 3 个重复 ACK 立即重传
到达乱序序号 + 接收缓冲重排只把连续段交付给应用
重复到达序号去重重复段直接丢弃并再确认一次
比特出错首部校验和16 位反码和,能力弱,应用层仍需自校验
接收方处理慢滑动窗口 rwnd、零窗口探测窗口为 0 时发送方停发,靠 persist 定时器探
网络整体拥塞拥塞窗口 cwnd、慢启动 / 拥塞避免 / 快恢复发送窗口取 min(rwnd, cwnd)

所以"TCP 保证可靠"准确的说法是:在连接未断、且对端进程还活着的前提下,TCP 保证字节流不丢、不重、有序。

2. 序号与确认怎么工作

发送方                                          接收方
seq=1001, len=1000  ---------------------------> 收到,回 ack=2001(期望下一个字节)
seq=2001, len=1000  ------ X 丢失
seq=3001, len=1000  ---------------------------> 收到,但中间有洞,回 ack=2001
seq=4001, len=1000  ---------------------------> 再回 ack=2001(重复 ACK)
                发送方收到 3 个重复 ACK,立即重传 2001 段(快速重传)
  • ack=n 是累积确认:n 之前的字节全部收到,n 是期望的下一个字节。
  • 累积确认的盲区是"缺一段要重传一大片",所以有 SACK(Selective ACK)选项,把已收到的区间告诉发送方。
  • RTO 由 RTT 采样动态算(Jacobson / Karels 算法),并带指数退避;重传过的样本会被 Karn 算法排除,避免把重传当往返。

3. 滑动窗口才是吞吐的来源

窗口让发送方可以"不等确认就连发",把链路从停等协议里救出来:

已发送已确认 | 已发送未确认(在途) | 可发送 | 不可发送
             <---- 发送窗口 = min(rwnd, cwnd) ---->
  • rwnd 由接收方通过首部窗口字段通告,是流量控制,防的是"发太快把接收缓冲打爆"。
  • cwnd 由发送方自己维护,是拥塞控制,防的是"发太快把网络打爆"。
  • 拥塞控制四阶段:慢启动(cwnd 指数增长)→ 拥塞避免(线性增长)→ 超时则 cwnd 降到 1 并重回慢启动 → 三个重复 ACK 则快重传 + 快恢复(cwnd 减半)。

4. 观测与调参

# 看单条连接的窗口、重传、RTT
ss -ti dst 10.0.0.8

# 常见内核参数
sysctl -w net.ipv4.tcp_retries2=8          # 重传次数上限,默认 15(约 15 分钟才断开)
sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456"
sysctl -w net.ipv4.tcp_wmem="4096 65536 4194304"
sysctl -w net.ipv4.tcp_sack=1

ss -ti 输出里 retrans 是重传计数、rtt 是平滑 RTT、cwnd 与 ssthresh 能直接看出连接是否被限速。 若 retrans 持续上涨而 cwnd 很小,多半是链路丢包而不是应用慢。

5. 常见追问

Q:TCP 可靠,业务就不会丢数据? 不会。应用崩溃、已 ACK 但未落盘、中间设备发 RST、对端进程被 kill,都会让数据"传到了但没生效"。 所以关键链路要应用层确认、幂等重试、落盘后再回 ACK。

Q:重传会一直重试吗? 不会。tcp_retries2 默认 15 次,累计约 15 分钟后内核放弃并断开连接,应用收到 ETIMEDOUT。 这也是为什么"卡住十几分钟才报错"的故障,往往要看这个参数。

Q:为什么还需要 QUIC 在 UDP 上重做一遍? TCP 的重传是连接级的:一个段丢了,后续数据即使到达也要等它补齐(队头阻塞)。 QUIC 把流拆开,每个流独立排序与重传,还能在用户态快速迭代拥塞算法,并用 0-RTT 省握手。

Q:校验和为什么不够? 16 位反码和的碰撞概率不低,且不覆盖伪首部之外的所有中间改写。 真正的完整性保障要靠 TLS 的 AEAD 或应用层摘要,TCP 校验和只是廉价的第一道筛子。

Q:为什么窗口会突然变小甚至为 0? 接收方应用读得慢,接收缓冲被填满,通告窗口降到 0;发送方进入 persist 状态周期探测。 若长期为 0,问题在消费端而非网络。

小结

TCP 的可靠性不是单点能力,而是"序号 + 确认 + 重传 + 窗口 + 拥塞控制"的组合拳: 序号解决乱序与重复,确认与重传解决丢失,校验和解决比特错误,窗口解决两端速度不匹配,拥塞控制解决整网过载。 理解每个机制挡住哪种故障,比背名词更能应对追问。

笔记加载中…