UDP协议与应用场景
约 1544 字大约 5 分钟
udpprotocol
2025-07-02
概述
UDP(User Datagram Protocol)是传输层的无连接协议,以简单和高效著称。它不提供可靠性保证、流量控制和拥塞控制,但正是这种"简陋"使其在实时性要求高的场景中成为首选。
UDP头部格式
UDP头部仅8字节,极其简洁:
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| Source | Destination |
| Port | Port |
+--------+--------+--------+--------+
| Length | Checksum |
+--------+--------+--------+--------+
| |
| Data (payload) |
| |
+------------------------------------+| 字段 | 长度 | 说明 |
|---|---|---|
| Source Port | 16 bits | 源端口号(可选,为0表示不需要回复) |
| Destination Port | 16 bits | 目标端口号 |
| Length | 16 bits | UDP报文总长度(头部+数据),最小为8 |
| Checksum | 16 bits | 校验和(IPv4可选,IPv6强制) |
与TCP头部对比:
UDP校验和
UDP校验和的计算范围包括伪头部、UDP头部和数据:
伪头部(12字节,用于计算但不传输):
+--------+--------+--------+--------+
| Source IP Address |
+--------+--------+--------+--------+
| Destination IP Address |
+--------+--------+--------+--------+
| Zero |Protocol| UDP Length |
+--------+--------+--------+--------+在IPv4中校验和是可选的(设为0表示未计算),但在IPv6中是强制的,因为IPv6头部本身不包含校验和。
UDP的核心特性
应用场景
DNS查询
DNS主要使用UDP端口53,因为查询通常很小(< 512字节),且需要低延迟。超过512字节或需要可靠传输时使用TCP。
# DNS查询使用UDP
dig @8.8.8.8 example.com
# 查看实际使用的协议
dig +tcp @8.8.8.8 example.com # 强制使用TCP实时视频/音频
视频流和VoIP对实时性要求极高,偶尔丢帧可以容忍,但延迟不可接受。
RTP over UDP 典型流程:
┌─────────┐ ┌─────────┐
│ 发送端 │ │ 接收端 │
│ 编码器 │ │ 解码器 │
├─────────┤ ├─────────┤
│ RTP │ ──UDP──→ │ RTP │
│ (负载) │ │ (负载) │
├─────────┤ ├─────────┤
│ RTCP │ ←──────→ │ RTCP │
│ (控制) │ │ (控制) │
└─────────┘ └─────────┘在线游戏
游戏需要快速同步玩家状态,过时的状态数据不值得重传:
# 游戏状态同步示例(Python)
import socket
import struct
import time
# 服务端
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('0.0.0.0', 9999))
while True:
data, addr = server.recvfrom(1024)
# 解析玩家输入
seq, x, y, action = struct.unpack('!Iffi', data)
# 广播游戏状态给所有玩家
state = struct.pack('!Iff', seq, new_x, new_y)
for player_addr in connected_players:
server.sendto(state, player_addr)QUIC协议
QUIC基于UDP实现了可靠传输,是HTTP/3的底层协议:
QUIC选择UDP的原因:
- 绕过中间设备(防火墙、NAT)对TCP的优化和干扰
- 在用户态实现,迭代速度快,不受内核更新限制
- 0-RTT连接建立
基于UDP的可靠传输
KCP协议
KCP是一个快速可靠的ARQ协议实现,牺牲带宽换取更低延迟:
// KCP核心参数配置
ikcpcb *kcp = ikcp_create(conv, user);
// 设置为极速模式
// nodelay=1: 启用nodelay模式
// interval=10: 内部处理间隔10ms
// resend=2: 快速重传触发次数
// nc=1: 关闭流控
ikcp_nodelay(kcp, 1, 10, 2, 1);
// 设置发送窗口和接收窗口
ikcp_wndsize(kcp, 128, 128);
// 设置MTU
ikcp_setmtu(kcp, 1400);KCP vs TCP延迟对比:
| 场景 | TCP平均延迟 | KCP平均延迟 | 改善 |
|---|---|---|---|
| 无丢包 | 20ms | 20ms | 0% |
| 1%丢包 | 40ms | 25ms | 37% |
| 5%丢包 | 120ms | 45ms | 62% |
| 10%丢包 | 300ms | 80ms | 73% |
RUDP设计要点
在UDP之上构建可靠传输需要实现:
// Go语言 RUDP包头设计
type RUDPHeader struct {
Flags uint8 // SYN/ACK/FIN/RST等标志
Channel uint8 // 通道号(可靠/不可靠/有序)
SeqNum uint16 // 序列号
AckNum uint16 // 确认号
Window uint16 // 窗口大小
Checksum uint32 // 校验和
}
// 通道类型
const (
ChannelUnreliable = 0 // 不可靠无序
ChannelReliableOrdered = 1 // 可靠有序
ChannelReliableUnordered = 2 // 可靠无序
)UDP编程要点
消息大小限制
理论最大值:65535 - 8(UDP头) - 20(IP头) = 65507 字节
实际建议值:不超过MTU(通常1500) - 28 = 1472 字节
原因:避免IP分片,分片会增加丢包概率多播编程
import socket
import struct
MCAST_GROUP = '239.1.1.1'
MCAST_PORT = 5007
# 发送端
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.sendto(b'Hello multicast', (MCAST_GROUP, MCAST_PORT))
# 接收端
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', MCAST_PORT))
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GROUP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
data, addr = sock.recvfrom(1024)缓冲区调优
# 查看当前UDP缓冲区
sysctl net.core.rmem_default
sysctl net.core.rmem_max
# 增大接收缓冲区(应对突发流量)
sysctl -w net.core.rmem_max=26214400
sysctl -w net.core.rmem_default=26214400
# 监控UDP丢包
cat /proc/net/snmp | grep Udp
# InErrors: 接收错误数
# RcvbufErrors: 因缓冲区满丢弃的包数
# SndbufErrors: 发送缓冲区错误数
# 实时监控
watch -n 1 "cat /proc/net/snmp | grep Udp"UDP vs TCP 选型指南
总结
UDP凭借简单高效的设计在实时通信、流媒体、游戏等领域不可替代。随着QUIC协议的普及,UDP在可靠传输领域也展现出强大潜力。理解UDP的特性和限制,合理选择使用场景,在需要时通过上层协议弥补可靠性缺失,是网络编程的重要能力。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于