TCP 与 UDP:核心区别、典型场景与前端关联
前端同学在排查“接口慢”“丢消息”“视频卡顿”“WebSocket 断开”这类问题时,常常会被问到:你知道这背后走的是 TCP 还是 UDP 吗?
这篇文档会用通俗方式把 TCP/UDP 讲清楚:它们分别解决什么问题、为什么差异这么大、各自适合什么场景,以及和前端常见技术(HTTP/3、WebSocket、WebRTC、DNS)的对应关系。
TCP 像“打电话”:先建立连接、按顺序、保证送达(更可靠)。
UDP 像“发短信/广播”:直接发出去、不保证到达与顺序(更轻量、更实时)。
1. 先把位置放对:它们在协议栈的哪一层?
TCP 和 UDP 都属于传输层协议(Transport Layer)。它们的上面是应用层协议(HTTP、WebSocket、DNS、WebRTC 的一部分等),下面是 IP(负责寻址与路由)。
如果你对“分层”的整体概念还不熟,建议先看:TCP/IP 体系结构(协议栈)入门。
2. TCP 是什么:可靠的“字节流”传输
TCP(Transmission Control Protocol)核心目标是:在不可靠的网络上,提供可靠、按序、无重复的传输。
2.1 你需要记住的 4 个关键词
- 面向连接:通信前要先建立连接,结束时要拆连接(连接是有“状态”的)。
- 可靠传输:通过确认应答(ACK)、超时重传、校验等机制尽量保证送达。
- 按序到达:接收端会按序重组数据(乱序到达也能拼回去)。
- 字节流:TCP 传的是“连续字节”,没有消息边界(这点非常关键)。
你 write 两次,并不意味着对方 read 会收到两次。对方可能一次读到两次的拼接,也可能一次只读到半条。
这也是很多“粘包/拆包”“SSE 消息粘连/截断”问题的根源之一。
想更深入理解 TCP 建连/断连过程,可以直接看:TCP 三次握手与四次挥手。
2.2 TCP 为什么“可靠”?
从学习角度,你可以把 TCP 的可靠性理解为“几套机制的组合拳”:
- 序列号(Sequence Number):给字节编号,方便按序重组、识别丢失。
- 确认应答(ACK):告诉对方“我收到了哪些字节”。
- 重传(Retransmission):没收到 ACK 就重发(超时/快速重传等)。
- 滑动窗口(Window)+ 流量控制:别把接收方撑爆(接收方告诉发送方“我还能收多少”)。
- 拥塞控制:网络拥堵时自动“降速”,避免把网络打爆(慢启动、拥塞避免等)。
3. UDP 是什么:轻量的“报文”传输
UDP(User Datagram Protocol)可以理解为:在 IP 之上做了端口封装的一层“尽力而为”传输。
3.1 UDP 的典型特征
- 无连接:发送方不需要先握手,想发就发(协议层面不维护连接状态)。
- 不保证可靠性:可能丢包、重复、乱序;也不会自动重传。
- 有消息边界:一次
send对应一个数据报(接收时按“报文”交付,而不是字节流)。 - 头部更小:开销更低(更“轻”)。
- 更适合多播/广播:工程上常用于广播、组播等场景(TCP 天生是点对点)。
UDP 不是“快”,而是“少做事”:不握手、不重传、不做排序/拥塞控制(这些要么不要,要么交给上层协议做)。
3.2 UDP 报文大小与 MTU:为什么 UDP 不适合“发大包”?
UDP 一次 send 对应一个数据报。如果数据报太大,超过链路能承载的 MTU(例如以太网常见 MTU 是 1500),就可能发生 IP 分片。
分片带来的问题是:
- 任何一个分片丢了,整条 UDP 报文就没了(接收端无法拼回完整数据报)。
- 某些网络设备/NAT/防火墙对分片不友好,分片更容易被丢弃。
因此工程实践里,基于 UDP 的协议通常会尽量控制单个包的大小,并在上层做分片/重组(或做路径 MTU 探测)。这也是你会看到很多“实时协议喜欢小包、频繁发”的原因之一。
4. TCP vs UDP:一张表快速对比
| 对比项 | TCP | UDP |
|---|---|---|
| 是否需要连接 | 是(面向连接) | 否(无连接) |
| 可靠性 | 高:确认/重传/按序/去重 | 低:尽力而为,可能丢/乱序/重复 |
| 传输单位 | 字节流(无边界) | 数据报(有边界) |
| 是否按序 | 是 | 不保证 |
| 头部开销 | 大(最小 20 字节起) | 小(8 字节) |
| 拥塞/流量控制 | 协议内置 | 协议不提供(需上层自律) |
| 典型应用 | HTTP/1.1、HTTP/2、WebSocket、SSH | DNS、音视频、实时游戏、QUIC(HTTP/3) 的承载层 |
注意:HTTP/3 是“跑在 UDP 上的 QUIC”,并不是“HTTP 直接用 UDP”。QUIC 在 UDP 之上自己实现了很多类似 TCP 的能力。
5. 为什么实时音视频/游戏更偏爱 UDP?
很多实时场景的目标不是“100% 不丢”,而是“尽量低延迟”。这会导致一个很直观的取舍:
- TCP 丢包后会重传,旧数据晚到可能仍然要按序交付 → 延迟抖动会变大。
- 音视频里“晚到的旧帧”往往没价值,宁可丢掉也要保证新帧及时到 → UDP 更适合。
但这不代表 UDP 就“什么都不管”。真实系统一般会在 UDP 之上再做一层(或多层)能力,例如:
- 序号/时间戳:识别乱序与过期数据。
- 抖动缓冲(jitter buffer):平滑网络抖动。
- 丢包恢复:重传(选择性)、前向纠错(FEC)等。
- 带宽估计与拥塞控制:避免把网络挤爆(不然你再实时也会卡死)。
前端相关例子:WebRTC 通常优先走 UDP 来承载媒体流(再叠加 DTLS/SRTP 等安全与媒体传输协议)。
6. HTTP/3 为什么基于 UDP:QUIC 在做什么?
一句话:QUIC 想要“像 TCP 一样可靠 + 像 UDP 一样灵活(易升级、低握手成本)”。
你可以把 QUIC 理解为“把 TCP 的很多能力搬到用户态,并与 TLS 深度融合”,再借助 UDP 作为“可被网络转发的载体”。它带来的常见收益包括:
- 更低的建连成本(握手更快,细节取决于是否复用已有密钥/会话)。
- 更好的多路复用体验:减少传统 TCP 层面某些阻塞带来的影响(尤其在丢包时的体验差异)。
- 更容易迭代:在用户态协议里更新,比内核里的 TCP 更灵活。
想看 HTTP 版本演进脉络可以参考:HTTP 版本演进。
7. 前端常见技术到底跑在谁上面?
把“前端常见协议/技术 → 传输层”的映射记住,排障会快很多:
- HTTP/1.1、HTTP/2:通常是 TCP(再叠加 TLS 就是 HTTPS)。
- HTTP/3:QUIC over UDP。
- WebSocket:TCP(本质是一次 HTTP Upgrade 后在同一条 TCP 连接上双向通信)。
- SSE:TCP(因此要注意字节流导致的消息粘连/截断问题)。
- WebRTC:多为 UDP 优先(媒体传输追求低延迟)。
- DNS:传统上多为 UDP;但在响应较大/需要可靠性时也会用 TCP。另外浏览器还可能使用 DoH(DNS over HTTPS,走 HTTP/TCP 或 HTTP/3/UDP)。
8. 一个小实验:用 Node 感受“字节流 vs 数据报”
同样是“发两次字符串”,TCP 端可能一次 data 事件就收到拼在一起的内容;UDP 端通常会收到两条独立的 message。
浏览器环境无法直接创建 TCP/UDP Socket;这里用 Node 做演示更直观。
8.1 TCP:两次 write,不等于两次 data
TCP 服务器(示例):
import net from 'node:net';
net
.createServer((socket) => {
socket.on('data', (chunk) => {
console.log('TCP 收到:', chunk.toString());
});
})
.listen(9000, () => console.log('TCP server on :9000'));
TCP 客户端(示例):
import net from 'node:net';
const socket = net.createConnection({port: 9000}, () => {
socket.write('A');
socket.write('B');
socket.end();
});
你可能会看到服务端打印一次 AB,也可能分两次打印 A、B(取决于时机与缓冲),这就是“字节流无边界”。
8.2 UDP:一次 send,对应一条 message(但可能丢)
UDP 服务器(示例):
import dgram from 'node:dgram';
const server = dgram.createSocket('udp4');
server.on('message', (msg) => console.log('UDP 收到:', msg.toString()));
server.bind(9001, () => console.log('UDP server on :9001'));
UDP 客户端(示例):
import dgram from 'node:dgram';
const client = dgram.createSocket('udp4');
client.send('A', 9001);
client.send('B', 9001, () => client.close());
UDP 更像“投递两封信”:接收端按“封”收到;但在真实网络下,信可能丢、可能顺序颠倒。
9. 常见误区纠正
- “UDP 一定比 TCP 快”:不一定。UDP 只是少了握手/重传等机制;一旦你在应用层补齐可靠性、拥塞控制,复杂度和开销也会上来(比如 QUIC)。
- “TCP 就不会丢包”:网络层仍然可能丢包,只是 TCP 会尽力重传让应用“看起来没丢”(但代价是更高延迟/更久等待)。
- “TCP 没有丢包所以不需要超时/重试”:应用层仍可能遇到连接断开、服务端崩溃、代理超时等问题,依然需要合理的超时与重试策略。
10. 总结
- TCP:可靠、按序、面向连接、字节流,适合“必须完整正确”的场景。
- UDP:轻量、无连接、数据报,适合“更关注实时”的场景(但要接受丢包/乱序,或在上层补齐能力)。
- 前端常见映射:HTTP/1.1/2 & WebSocket & SSE → TCP;HTTP/3(QUIC) & WebRTC 媒体 → UDP 为主。
面试高频问答
Q1:TCP 和 UDP 的核心区别是什么?
TCP 面向连接、可靠、按序、字节流;UDP 无连接、尽力而为、可能丢/乱序、数据报有边界。
Q2:为什么说 TCP 是“字节流”,这会带来什么问题?
TCP 不保留应用层消息边界:发送两次不等于接收两次,可能粘在一起或被拆开。
解决思路通常是自己做协议:定长包、分隔符、长度字段(Length-Prefix)等。
Q3:UDP 完全不可靠吗?还能用于“可靠传输”吗?
UDP 本身不保证可靠,但可以在上层实现可靠机制(序号、ACK、重传、窗口、拥塞控制等)。QUIC 就是典型例子。
Q4:为什么 DNS 常用 UDP?什么时候会用 TCP?
UDP 开销小、一次请求/响应模型简单。
当响应较大、需要可靠传输、或某些特定场景(如区域传送)时会用 TCP。现代浏览器也可能走 DoH/DoT(本质走 HTTPS/TLS)。
Q5:为什么实时音视频更倾向用 UDP?
实时场景更怕“晚到”,旧数据晚到价值不大;TCP 的重传和按序交付可能放大延迟抖动。UDP 更灵活,允许丢弃过期数据并用抖动缓冲/FEC 等手段优化体验。
Q6:HTTP/3 为什么不直接用 TCP,而要用 UDP?
HTTP/3 依赖 QUIC。QUIC 借助 UDP 在用户态实现“可靠 + 加密 + 多路复用”等能力,并降低建连成本、提升在丢包时的体验,且更容易迭代升级。
Q7:WebSocket 是 TCP 还是 UDP?为什么?
WebSocket 基于 TCP:先通过 HTTP Upgrade 建立连接,之后在同一条 TCP 连接上做全双工通信,保证消息按序到达(但仍要注意应用层心跳与断线重连)。
Q8:前端怎么判断当前请求走的是 HTTP/2 还是 HTTP/3?
一般可在浏览器 DevTools 的 Network 面板查看协议列(不同浏览器展示略有差异)。如果看到 h2 多半是 HTTP/2;如果看到 h3/quic 则是 HTTP/3(QUIC over UDP)。