TCP 与 UDP
TCP 提供可靠、有序的字节流,UDP 提供保留消息边界的数据报。两者的区别不只是有没有握手,而是传输层替应用承担了哪些工作:TCP 负责确认、重传和发送调节,UDP 把这些选择留给上层协议。
要根据场景选择协议,需要先说明应用要什么:数据必须完整按序到达,还是过时的数据可以丢弃;消息边界由谁识别,丢包后又由谁恢复。
1. 面试核心
先记住这几条,再读正文- TCP 提供可靠、有序的字节流,UDP 提供保留边界的数据报;选择时要看交付要求和应用愿意承担的恢复成本。卡 · TCP 与 UDP 概述
- TCP 用字节序列号、确认和重传恢复传输。累计 ACK 指向下一个期望字节,SACK 补充报告缺口之后已经收到的区间。卡 · 累计确认与重传
- 流量控制用 rwnd 保护接收端缓冲区,拥塞控制用 cwnd 调节进入网络的数据量;在途数据受两者较小值约束。卡 · 流量控制与拥塞控制
- UDP 头部固定为 8 字节,协议本身不负责确认、重传和拥塞控制,需要时由上层实现。
- TCP 不保留应用消息边界,一次
send的数据可能分多次读出,多次发送也可能合在一次读取中。
2. TCP 与 UDP 概述
2.1 TCP 与 UDP 的区别
字节流是按顺序交付的一串字节,不标记每次应用发送的边界;数据报则把一次发送的数据作为独立消息,接收方仍能分清这一条和下一条。TCP 与 UDP 的其他差异可以从这两种交付形式展开:
| 特性 | TCP | UDP |
|---|---|---|
| 交付形式 | 有序字节流。 | 保留边界的数据报。 |
| 连接状态 | 双方维护连接状态。 | 协议没有握手连接。 |
| 恢复机制 | 确认、重传与去重。 | 交由应用协议处理。 |
| 发送控制 | 接收窗口和拥塞控制。 | 应用承担速率与拥塞治理。 |
| 首部 | 最少 20 字节,带选项时更长。 | 固定 8 字节。 |
UDP socket 也可以调用 connect() 指定对端,但这只是配置本地 socket,不会因此在网络上增加一次 UDP 握手。
2.2 使用场景
文件传输、数据库连接需要完整按序的数据,可以直接使用 TCP。实时音视频则可以用 UDP,由上层根据时效决定是否重传:一帧画面已经过时,等待重传可能不如丢弃它、继续播放后面的内容。UDP 也可以用于组播。
不能仅凭应用名称推断传输协议。DNS 同时支持 UDP 和 TCP;HTTP/3 虽然使用 UDP,可靠流却由其上的 QUIC 提供。要看的是整个协议栈最终向应用提供什么保证。
3. TCP
3.1 传输故障
TCP 要处理底层传输中的丢失、重复、乱序和损坏,并把有效字节按顺序交给应用。「可靠」不代表任何网络故障都能恢复;连接持续失败时,TCP 最终仍会向应用报告错误。
3.2 TCP 的可靠性设计
TCP 按字节编号,再把一段连续字节装进报文段发送。接收端根据序列号知道数据放在哪里、哪些已经收到、哪些仍然缺失,从而处理乱序和重复;校验和用来检测传输错误,但不能保证发现所有损坏。
接收端通过 ACK 告诉发送端下一个期望的字节序号。发送端在数据获得确认前保留副本,需要时重新发送。两个方向最初从哪个序号开始,则由握手同步,见 TCP 连接。
3.3 TCP 头部
TCP 头部携带端口、字节位置、接收能力和控制标志,让对端知道这段数据属于哪条连接、应该怎样处理:
| 字段 | 含义 |
|---|---|
| 源端口、目标端口 | 与源目标 IP 一起标识连接。 |
| Sequence Number | 当前段首字节的序号;SYN 使用初始序号。 |
| Acknowledgment Number | 期望收到的下一个字节序号。 |
| Window | 接收端通告的可用窗口。 |
| Checksum | 检测首部和数据的传输错误。 |
| SYN、ACK、FIN、RST | 标记连接建立、确认、结束和复位。 |

3.4 累计确认与重传
累计确认是用一个 ACK 确认此前所有连续收到的字节,确认号指向下一个期望字节。假设字节 1000 至 1499 都已连续收到,接收端回复 ACK=1500。接下来 1500 至 1999 丢失,即使 2000 至 2499 已到达,ACK 也仍然是 1500,因为中间还有缺口。
SACK(选择性确认)补充报告已经收到的其他区间。例如,接收端在累计 ACK 之外说明 2000 至 2499 已收到,发送端就能据此缩小重传范围。SACK 不改变累计 ACK 的含义。
发送端不能无限等待确认。RTO 是重传超时时间,根据往返耗时 RTT 及其波动估计;定时器到期后会重传,并增加下一次等待时间。也不必总等超时:RFC 5681 的快速重传在收到三个重复 ACK 后触发,RFC 8985 的 RACK 则根据时间判断哪些数据可能已丢失。
这里的 ACK 只确认对端协议栈收到了字节。数据库是否提交、订单是否创建,还要由应用响应确认。
3.5 流量控制与拥塞控制
流量控制防止发送方把接收端缓冲区塞满,拥塞控制则防止发送方往网络中注入过多数据。它们分别看两个窗口:
- 接收窗口
rwnd由接收端通告,说明它还能接收多少数据。 - 拥塞窗口
cwnd由发送端维护,根据网络反馈限制发送量。
简化计算时,已发出但尚未确认的在途数据不能超过两者较小值。例如,rwnd 是 64KB、cwnd 是 16KB,已有 12KB 在途,那么最多只能再发送 4KB,而不是再发 16KB。
rwnd 为 0 时,发送端暂停普通数据发送,并用零窗口探测确认窗口是否恢复。接收端应用读取数据、释放缓冲区后,便可以重新通告可用窗口。
cwnd 的调整取决于拥塞控制算法。RFC 5681 描述的慢启动会快速扩大窗口,进入拥塞避免后增长放缓,检测到丢包后则调整窗口并恢复传输。CUBIC、BBR 等算法各有规则,不能把一种算法的增减方式套到所有 TCP 连接上。
3.6 小包合并与延迟确认
Nagle 算法在已有未确认数据时暂存后续的小写入,等收到 ACK 或攒够一个完整段后再发送,以减少小包。延迟 ACK则是接收端暂缓确认,等待合适时机一起发送 ACK。
两者可能形成额外等待:发送端等 ACK 才发后半段小请求,接收端却还在延迟发送 ACK,应用就会晚一些收到完整请求。Linux 中可以用 TCP_NODELAY 关闭 Nagle,但这可能增加小包数量和 CPU 开销,要结合实际写入模式判断。无论是否关闭 Nagle,TCP 都不会保留应用消息边界。
4. UDP
4.1 传输控制开销
UDP 本身不握手,也不维护确认和重传状态,因此协议控制开销较少。但应用如果需要可靠性,就仍要在上层实现这些机制;它们的成本不会因为选了 UDP 而消失。TCP 则可以通过长连接,让多个请求分摊一次握手的开销。
4.2 UDP 的特点
UDP 保留消息边界,却不保证数据报必达、只到一次或按序到达。应用可以按需求丢弃过期媒体帧,也可以自行确认和重传。
保留边界也不意味着接收缓冲区可以任意小。在 Linux 中,如果传给 recv() 的用户缓冲区装不下一个 UDP 数据报,数据报会被截断;剩余部分被丢弃,不会留到下一次 recv() 再补齐。
4.3 UDP 的头部
UDP 的 8 字节头部由四个字段组成:源端口、目标端口、长度和校验和。其中长度包含头部和数据,校验和用于检测错误。头部没有序列号和确认号,丢包后的恢复仍由上层协议负责。
过大数据报的分片和路径 MTU 处理见 IP。
5. 协议选择
选择协议时,先确定哪些数据必须完整交付,哪些数据过时后可以放弃。需要可靠、有序字节流时,TCP 已经提供了相应机制;需要自行决定恢复方式、保留数据报边界时,可以考虑 UDP,但要承担必要的重传和拥塞控制。最后再结合时延要求,以及部署环境是否支持所选协议作判断。
6. 常见问题
6.1 UDP 一定比 TCP 快吗?
不一定。UDP 头部更小、没有协议握手,但最终耗时还取决于网络丢包、应用如何恢复,以及是否需要等待完整数据。TCP 长连接已经分摊了握手成本;如果 UDP 上层又实现确认和重传,就要比较整个协议栈的效果。
6.2 TCP 一次发送,为什么不能保证一次读完?
因为 TCP 只保证字节顺序,不记录每次应用调用 send 的边界。比如连续发送两条各 100 字节的消息,接收方一次可能只读到 50 字节,也可能同时读到 200 字节。应用需要长度字段或其他分帧规则才能还原两条消息,具体实现见 RPC。
7 · 面试题卡片
本页题目与参考回答
7.1 TCP 和 UDP 有哪些差异,实际项目中怎样选择
考察重点
- 连接、可靠性、有序性和消息边界。
- 流量控制、拥塞控制与应用层责任。
- DNS、实时音视频和 QUIC 等实际场景。
相关内容:TCP 与 UDP 概述、协议选择。
参考回答
TCP 提供面向连接的可靠有序字节流;UDP 保留数据报边界,但不提供确认、重传和拥塞控制。
需要完整按序交付的文件传输,可以直接使用 TCP;实时媒体可以使用 UDP,由上层决定过时数据是否还值得重传。TCP 不保留应用消息边界,仍需协议自己定义分帧规则。
选择时要看整个协议栈提供的保证。例如 HTTP/3 使用 UDP,却由 QUIC 提供可靠流,所以不能只按「TCP 可靠、UDP 不可靠」就判断应用能力。
7.2 流量控制与拥塞控制分别限制什么?
考察重点:rwnd、cwnd 和零窗口探测。
相关内容:流量控制与拥塞控制。
参考回答
流量控制用 rwnd 限制发送量,防止接收端缓冲区溢出;拥塞控制用 cwnd 调节进入网络的数据量。
在途数据受两者较小值限制,还能发送多少,要再扣除已经在途的数据。rwnd 为 0 时暂停普通数据发送,并通过零窗口探测确认是否恢复;网络拥塞则由相应算法根据反馈调整 cwnd。
7.3 累计 ACK、SACK 和超时重传怎样处理丢包?
考察重点:字节编号、连续确认和恢复路径。
相关内容:累计确认与重传。
参考回答
累计 ACK 说明连续收到哪里,SACK 补充报告其他已收到区间;确认迟迟不到时,由重传定时器触发恢复。
累计 ACK 指向下一个期望字节,只要前面还有缺口,就不会因后续数据到达而前移。SACK 帮助发送端区分缺失和已收到的区间,减少不必要的重传。RTO 根据 RTT 及其波动估计,超时后重传,并延长下一次等待时间。
7.4 小请求为什么会受 Nagle 和延迟 ACK 影响?
考察重点:小写入合并与确认等待。
相关内容:小包合并与延迟确认。
参考回答
Nagle 可能在等 ACK 才发送后续小数据,而接收端又在延迟发送 ACK,两者叠加就会增加等待。
已有未确认数据时,Nagle 暂存后续小写入,收到 ACK 或攒够完整段后再发送。如果这些字节恰好是一条请求的剩余部分,应用收到完整请求的时间就会推迟。TCP_NODELAY 可以关闭 Nagle,但可能增加小包开销,配置前应观察实际写入模式和延迟。
8. 参考资料
- RFC 9293:Transmission Control Protocol
- RFC 768:User Datagram Protocol
- RFC 9000:QUIC
- RFC 5681:TCP Congestion Control
- RFC 6298:TCP Retransmission Timer
- RFC 2018:TCP SACK
- RFC 896:Nagle Algorithm
- Linux man-pages:tcp(7)
- Linux man-pages:udp(7)
- RFC 8985:RACK-TLP
来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明