先说个真实感受:做了这么多年网络协议相关的开发,身边同学、同事聊到 UDP,最常听到的评价就是“不可靠的传输协议”一句话总结。这句话本身没错,但它把 UDP 说成了一个“低配版 TCP”,这是我一直不太认同的。UDP 不是“做不好可靠”,而是“故意不帮你做可靠”。这两种理解在工程上的差别非常大。这篇内容里,我会把 UDP 从报头结构、协议设计,到 DNS、音视频、游戏这些实际应用,再到 socket 编程和线上调优踩坑,完整梳理一遍。适合对网络协议有一定基础、但想在真实项目里把 UDP 用明白的后端开发、网络工程师和运维同学。看完你至少能回答三个问题:它为什么快、它为什么“丢”、以及你该在什么场景下选它。
1. UDP 到底做了什么——先搞懂它的“三个没有”
1.1 报头短到只有 8 个字节
UDP 的报头结构非常简单,固定 8 字节,由四部分组成:源端口(16 位)、目的端口(16 位)、报文长度(16 位)、校验和(16 位)。对比 TCP 那个至少 20 字节起步、带序号确认号各种标志位的报头,UDP 可以说是“极简主义”的典型代表。
这个设计不是拍脑袋决定的。UDP 的目标从来就不是“提供更多服务”,而是“以最低成本把数据报从 A 送到 B”。既然不需要序号、不需要确认号、不需要窗口大小,那这些字段就完全没有存在的必要。长度字段甚至对应用层来说都是可以推导出来的——因为 IP 头里有总长度,减掉 IP 头长度就是 UDP 报文长度,之所以保留这个冗余,就是为了让 UDP 自身能独立解析报文边界。
我在跟不少刚入行的同学讲这块的时候,喜欢用一个类比:TCP 像寄挂号信,要签收、要回执、丢了要补寄、顺序乱了要重新排;UDP 像往邮筒里扔明信片,你扔出去就不管了,对方收没收到、收到几张、顺序对不对,全靠双方自己想办法。明信片便宜、快、不占资源,但任何额外服务都得你自己加钱升级。这个类比基本能解释 UDP 七成的设计哲学。
1.2 无连接、无状态、无确认,分别意味着什么
这三个“没有”是理解 UDP 的关键,挨个说。
无连接,意味着发送方不需要提前跟接收方建立会话。TCP 要三次握手才能开始传数据,UDP 直接就是“我想发就发,封装好报文丢给对端”。这让 UDP 天然适合那些“问一句答一句”的场景,比如 DNS 查询,也适合海量设备低频上报的场景,比如传感器每隔几秒发一条状态,如果每个设备都先握手,连接表会被撑爆。
无状态,意味着协议栈不为每个 UDP 报文维护上下文。TCP 连接是内核里的一个实体,有发送缓冲区、接收缓冲区、拥塞窗口、往返时间估计这些东西。UDP 没有,内核处理一个 UDP 报文就是查一下目的端口有没有进程在听,有就丢进接收队列,没有就回一个 ICMP 端口不可达。这个“不维护状态”的特性带来两个好处:一是内存占用极低,二是处理耗时稳定。很多高并发游戏服务器愿意用 UDP,就是因为连接状态全部放在应用层用哈希表管理,比让内核管几万个 TCP 连接要可控得多。
无确认,是 TCP 可靠性的根基,但也是延迟的根源。TCP 发一个包要等 ACK,超时没等到就重传,窗口满了就停。UDP 没有这套机制,发出去就是发出去了,应用层想知道“对方收到没有”,只能自己在下层定义一套 ACK 机制,或者干脆接受“偶尔丢一个包没关系”。这个“把可靠性的决定权交给应用层”的取舍,恰恰是 UDP 最大的价值所在。
1.3 校验和:UDP 唯一的一道及格线
很多人以为 UDP 完全没有可靠性保障,这不对。UDP 至少提供校验和,虽然它的加解密强度不高,而且 IPv4 下还可以关闭(IPv6 下是强制的),但它确实是 UDP 报头里唯一跟“内容正确性”相关的东西。
校验和的计算范围覆盖伪首部、UDP 报头和负载数据。伪首部很有意思,它不是真实的报文头部,而是从 IP 头里取源地址、目的地址、协议号、UDP 长度拼出来的一个临时结构,目的是让 UDP 校验和能覆盖到 IP 层的地址信息,防止报文被路由到错误的地址还浑然不知。这里有个经典坑:如果数据是奇数长度,计算校验和时要补一个零字节,但这个补的字节不参与传输,只是参与计算,我第一次 debug 这块的时候一度非常困惑。
校验失败的时候,UDP 的处理策略是直接静默丢弃,不会通知发送方,也不会通知应用层。你在用户态 recvfrom 可能永远等不到这个报文,而且你也不知道它到底是被中间设备丢了,还是到了本机但校验没通过被扔了。这个“静默丢弃”的设计在节省带宽上是合理的,但对排障来说非常不友好,后面讲排查技巧的时候我会再提。
2. UDP 和 TCP 怎么选——先别急着背“一个可靠一个不可靠”
2.1 一张表说清核心差异
选协议之前,先把两种协议的核心差异放一张表里,方便对照。
维度
TCP
UDP
连接状态
面向连接,需三次握手
无连接,即发即走
可靠性
确认、重传、序号保证
只保证校验和,不保证送达
数据边界
字节流,应用层自己分包
报文边界保留,一次读一个包
有序性
保证顺序
不保证顺序
拥塞控制
有,窗口动态调整
无,发送速率完全由应用控制
头部开销
最少 20 字节
固定 8 字节