跳到主要内容
约 8 分钟4 张卡

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 的其他差异可以从这两种交付形式展开:

特性TCPUDP
交付形式有序字节流。保留边界的数据报。
连接状态双方维护连接状态。协议没有握手连接。
恢复机制确认、重传与去重。交由应用协议处理。
发送控制接收窗口和拥塞控制。应用承担速率与拥塞治理。
首部最少 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标记连接建立、确认、结束和复位。

TCP 头部示例,来源 Wikipedia

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 一次发送,为什么不能保证一次读完?​

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. 参考资料​

来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明