TCP 是如何保证可靠传输的?
一句话回答
IP 层只负责尽力投递,TCP 在它之上用几套机制保证数据不丢、不错、不乱、不重:序列号和确认应答让发送方知道哪些数据已经送达;超时重传、快速重传和 SACK 补发丢失的数据;校验和发现损坏的数据;接收方按序号排序、去重。此外还有流量控制(滑动窗口)防止发送方把接收方淹没,拥塞控制防止把网络塞满。
详细解析
序列号和确认应答
TCP 给发送的每个字节都编了号。报文段的 seq 是这段数据第一个字节的序号;接收方回复的 ack 是它期望收到的下一个字节的序号,表示在这之前的数据都收到了。
发送方 接收方
|--- seq = 1, len = 1000 ------------------------>|
|<--------------------------------- ack = 1001 ---|
|--- seq = 1001, len = 1000 --------------------->|
|<--------------------------------- ack = 2001 ---|
这种确认是累积确认:收到 ack = 2001 就说明前 2000 个字节全部到了,所以中间某个 ACK 丢了也没关系。
重传机制
| 机制 | 触发条件 | 说明 |
|---|---|---|
| 超时重传 | 数据发出后,超过重传超时时间(RTO)还没收到确认 | RTO 根据测得的往返时间(RTT)及其波动动态计算;每次超时重传后 RTO 翻倍 |
| 快速重传 | 连续收到 3 个重复的 ACK | 接收方收到乱序的数据段时,会重复确认它还在等的序号。3 个重复 ACK 说明中间丢了一段、后面的已经到了,不必等超时,立即重传 |
| 选择性确认(SACK) | 双方在握手时协商启用 | 接收方告诉发送方哪些不连续的数据段已经收到,发送方只重传缺失的部分;没有 SACK 时,发送方不知道后面哪些已经到了,可能重传对方已有的数据 |
校验和、排序和去重
- 校验和:覆盖 TCP 首部、数据,以及由源 IP、目的 IP 等组成的伪首部。校验失败的报文段直接丢弃、不确认,之后由重传补上
- 排序:数据段可能乱序到达,接收方按序号排好,再按顺序交给应用
- 去重:重传可能让同一段数据到达两次,接收方根据序号丢弃重复的部分
TCP 的校验和只有 16 位,检错能力有限,对完整性要求高的场景还要依靠 TLS 等上层机制。
流量控制:滑动窗口
接收方的缓冲区是有限的。它在每个 ACK 里通告接收窗口 rwnd,也就是自己还能接收多少数据;发送方已发送但未确认的数据量不能超过这个值。下图中每格代表一段数据:
1 2 3 4 5 6 7 8 9 10 11 12
+---+---+---+---+---+---+---+---+---+---+---+---+
| A | A | A | B | B | B | C | C | C | D | D | D |
+---+---+---+---+---+---+---+---+---+---+---+---+
|<---- window = 6 ----->|
A:已发送、已确认
B:已发送、未确认
C:未发送,但在窗口内,可以立即发送
D:未发送,超出窗口,要等窗口右移
收到 ack = 6(4、5 已确认)后,窗口左边界移到 6;如果接收方通告的窗口仍然是 6,右边界就移到 11,10 和 11 变成可发送。窗口就这样随着确认不断向右"滑动"。
如果接收方的应用读得慢、缓冲区满了,它会通告 rwnd = 0,发送方暂停发送。接收方腾出空间后会发送窗口更新,但这个报文可能丢失,双方就会互相等待。所以窗口为 0 时,发送方会定期发送窗口探测报文,询问最新的窗口大小。
拥塞控制和连接管理
面试官可能追问
流量控制和拥塞控制有什么区别?
流量控制照顾的是接收方的处理能力:窗口 rwnd 由接收方根据缓冲区的剩余空间在 ACK 中通告。拥塞控制照顾的是整个网络的承载能力:拥塞窗口 cwnd 由发送方根据丢包、延迟等信号自己估算。发送方实际能发送的数据量取两者中的较小值。
TCP 的队头阻塞是什么?
TCP 必须按顺序把数据交给应用。前面的数据段丢了,后面已经到达的数据只能留在接收缓冲区里,等丢失的段重传成功后才能一起交给应用。HTTP/2 在一个 TCP 连接上复用所有请求,一个包丢失会卡住全部请求;HTTP/3 改用 QUIC 解决了这个问题,见 HTTP 版本区别。
Nagle 算法是什么?
它把小数据包攒起来再发送:只要还有已发送但未确认的数据,新产生的小数据就先缓存,等收到 ACK 或者攒够一个 MSS 再发,避免网络上充斥只带几个字节的小包。代价是增加延迟,和接收方的延迟确认叠加时更明显。游戏、实时交互这类对延迟敏感的场景,可以用 TCP_NODELAY 选项关闭它,Node.js 中对应 socket.setNoDelay(true)。
易错点
- ack 是"期望收到的下一个字节的序号",不是"已收到的最后一个字节的序号"
- RTO 不是固定值:它根据 RTT 动态计算,每次超时重传后还会翻倍
- TCP 的可靠只到传输层:数据进了对方的接收缓冲区,不代表对方应用已经处理成功,业务上的"必达"还要靠应用层确认
AI 模拟面试官
用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮
这道题你掌握了吗?
选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。
学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。