这是《计算机网络 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 UDP 是无连接、不可靠、面向数据报的传输协议。它没有连接建立、重传、排序、流量控制和拥塞控制,因此代价小、延迟低,但也把可靠性问题交给了应用层。DNS、NTP、视频通话、游戏和 QUIC 都运行在 UDP 上。
7.1 UDP 头部
UDP 头部只有 8 字节:
| Source Port | Destination Port |
| Length | Checksum |
与 TCP 相比:
| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 有 | 无 |
| 可靠 | 重传确认 | 尽力而为 |
| 顺序 | 字节流有序 | 数据报可能乱序 |
| 流控 | 滑动窗口 | 无 |
| 拥塞控制 | 有 | 无 |
| 头部 | 至少 20 字节 | 8 字节 |
| 边界 | 字节流 | 保留数据报边界 |
UDP 保留消息边界。发送两次数据报,接收方通常按两次读取;TCP 则是连续字节流,需要应用协议划分消息。
7.2 UDP 适用场景
适合:
- 实时音视频,宁可丢帧也不愿等待重传;
- 游戏同步,低延迟优先;
- DNS 查询,一问一答;
- 广播或多播;
- 内网服务发现;
- 作为 QUIC 的底层承载;
- 高频遥测,允许少量丢失。
不适合:
- 转账、下单等强一致业务;
- 大文件可靠传输;
- 需要严格顺序且不可丢的日志;
- 未经治理的公网大规模服务;
- 没有应用层确认与限流的请求响应。
7.3 使用 UDP 的代价
UDP 简化了协议,但复杂度不会消失,而是转移到应用层:
| 问题 | 应用层方案 |
|---|---|
| 丢包 | 序号、ACK、重传或 FEC |
| 乱序 | 序号重组 |
| 重复 | 去重 |
| 流控 | 接收窗口或速率限制 |
| 拥塞控制 | 自定义 CC 或基于带宽探测 |
| 身份安全 | DTLS 或自定义加密 |
| 连接管理 | 会话 ID、心跳、超时 |
QUIC 正是这种思路:基于 UDP,在用户态实现可靠流、多路复用、加密和现代拥塞控制。
7.4 Linux UDP 缓冲区
UDP 没有发送缓冲的重传语义,但内核仍需要队列。
查看:
sysctl net.core.rmem_max
sysctl net.core.wmem_max
sysctl net.core.rmem_default
sysctl net.core.wmem_default
查看统计:
ss -lun
netstat -su
关注:
RcvbufErrors:接收缓冲不足;SndbufErrors:发送缓冲不足;InErrors:校验和等错误;InDatagrams/OutDatagrams;receive errors增长。
如果 UDP 接收方处理慢,包会被直接丢弃,不会像 TCP 一样通过窗口通知发送方减速。
7.5 UDP 编程示例
服务端:
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("0.0.0.0", 9000))
while True:
data, addr = server.recvfrom(4096)
server.sendto(b"ack:" + data, addr)
客户端:
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.settimeout(2)
for i in range(3):
client.sendto(f"ping-{i}".encode(), ("127.0.0.1", 9000))
try:
data, _ = client.recvfrom(4096)
print(data.decode())
except socket.timeout:
print("timeout")
如果没有超时,一次丢包就可能让客户端永久等待。
7.6 NAT 与 UDP
UDP 无连接,NAT 映射通常依赖超时维护:
内网主机发送 UDP
-> NAT 创建五元组映射
-> 空闲超时后删除
-> 对端再发送无法进入
应用层常见方案:
- 心跳保活;
- STUN 获取公网映射;
- TURN 中继;
- ICE 收集候选地址;
- 连接迁移时使用会话标识。
QUIC 使用 Connection ID 帮助在地址变化后恢复连接,但路径和 NAT 仍需正确处理。
7.7 UDP 排障
# 查看监听
ss -lunp
# 测试连通性
nc -u -v 10.20.1.10 9000
# 抓包
tcpdump -i eth0 udp port 9000 -nn
# 统计
netstat -su
排障要点:
- 双端是否都使用 UDP;
- 是否单向可达;
- 安全组是否放行;
- 接收队列是否溢出;
- 应用是否阻塞;
- 数据报是否超过路径 MTU;
- NAT 映射是否超时;
- 是否存在丢包和乱序。
本章小结
UDP 提供低开销、无连接的数据报传输,保留消息边界,但不提供可靠、有序、流控和拥塞控制。实时音视频、DNS、游戏和 QUIC 适合 UDP,而关键业务必须由应用层补足可靠性。UDP 排障要关注接收缓冲丢弃、单向可达、NAT 超时和路径 MTU。
思考题
- TCP 和 UDP 对消息边界的处理有什么不同?
- 为什么实时视频可以接受少量丢包?
- UDP 接收方处理慢会发生什么?
- QUIC 为什么基于 UDP 而不是直接修改 TCP?
- 设计一个可靠的 UDP 协议需要哪些字段和机制?