1. UDP协议基础回顾:UDP的特点、UDP与TCP的区别、UDP的应用场景

聊粘包问题之前,咱们得先把UDP这个老朋友请出来好好盘一盘。说实话,我见过不少刚入行的朋友,一提到UDP就觉得它「不可靠」,然后就敬而远之。其实这种看法有点片面。

UDP全称是User Datagram Protocol,用户数据报协议。它工作在传输层,和TCP平级。但它的设计哲学和TCP完全相反——TCP追求的是「可靠」,UDP追求的是「快」。

1.1 UDP的核心特点

UDP的特点,我用一句话就能概括:无连接、不可靠、无状态、有边界。咱们一个一个说。

  • 无连接:发数据前不需要握手。你想发就发,对方想收就收。没有三次握手,也没有四次挥手。
  • 不可靠:数据丢了就丢了,UDP不会重传。它也不保证数据顺序,后发的可能先到。
  • 无状态:UDP socket不维护连接状态。发完一个报文,它就把这事忘了。
  • 有边界:这一点特别重要。UDP保留消息边界,一次sendto对应一次recvfrom。不会出现「半个包」的情况。

关键点:UDP的「有边界」特性,决定了它天然不存在TCP那种粘包问题。但别高兴太早——应用层的「逻辑包」粘在一起,UDP可管不了。

我记得刚入行那会儿,有个老工程师跟我说:「UDP就像寄明信片,你写好地址扔进邮筒,至于能不能到、什么时候到、到了是不是完整的,你都不知道。」这个比喻我一直记到现在。

1.2 UDP与TCP的核心区别

TCP和UDP的区别,我建议你从「设计目标」去理解。TCP要的是可靠传输,UDP要的是低延迟传输。目标不同,实现自然不同。

对比维度 UDP TCP
连接状态 无连接 面向连接
可靠性 不可靠,不重传 可靠,超时重传
数据边界 保留消息边界 字节流,无边界
传输顺序 不保证有序 保证有序
流量控制 无 滑动窗口
拥塞控制 无 有(慢启动、拥塞避免)
首部开销 8字节 20~60字节
传输模式 支持广播、多播 仅单播

你看这个表格,UDP的首部只有8个字节,TCP最少20个。为什么?因为UDP不需要维护那么多状态信息。源端口、目的端口、长度、校验和,就这四个字段。

我的经验:做实时音视频传输时,我从来不用TCP。延迟受不了。UDP丢个几帧画面,用户根本感觉不到。但如果是文件传输,我肯定选TCP——丢一个字节文件就坏了。

1.3 UDP的应用场景

UDP适合哪些场景?说白了,就是那些「可以容忍少量丢包,但不能容忍高延迟」的场景。

  • 实时音视频通信:VoIP、视频会议、直播。丢几帧画面没事,卡顿才要命。
  • 在线游戏:尤其是FPS、MOBA类游戏。玩家的操作指令必须低延迟送达。
  • DNS查询:DNS默认使用UDP 53端口。一个查询一个响应,简单高效。
  • DHCP:设备启动时连IP都没有,怎么建立TCP连接?UDP无连接的特性正好派上用场。
  • SNMP:网络设备监控,轻量级,不需要可靠传输。
  • 广播/多播应用:UDP天然支持一对多通信,TCP做不到。

我曾经在一个物联网项目里用过UDP。传感器每隔100ms上报一次数据,丢几个包没关系,但延迟超过500ms就会触发告警。这种场景,TCP的拥塞控制和重传机制反而成了负担。

1.4 UDP报文结构

理解UDP的粘包问题,得先知道UDP报文长什么样。我画了一张图,你看一眼就明白了。

UDP 数据报结构 IP首部 (20字节) 源IP / 目的IP / 协议号 UDP首部 (8字节) 源端口(2) | 目的端口(2) 长度(2) | 校验和(2) UDP数据 (应用层报文) 最大 65507 字节 ⚠ 注意:UDP数据报总长度 = IP首部(20) + UDP首部(8) + 应用数据 以太网MTU=1500字节,UDP数据超过1472字节时会在IP层分片 UDP消息边界示意 sendto("A") sendto("B") sendto("C") 接收端: recvfrom() 三次 分别收到 A、B、C

从这张图能看出什么?UDP数据报是一个完整的、有边界的独立单元。发送端调用一次sendto,接收端调用一次recvfrom,拿到的就是完整的数据报。不会出现TCP那种「发了两次,收了一次,数据粘在一起」的情况。

注意:UDP虽然不会在传输层粘包,但应用层如果连续发送多个小报文,接收端可能一次recvfrom只读一部分,剩下的数据就丢了。这不是粘包,是「丢包」——UDP的recvfrom默认只读取一个完整数据报,读不完的部分直接丢弃。

1.5 为什么讲UDP粘包?

你可能会问:「UDP不是有边界吗?怎么还会有粘包问题?」

嗯,这个问题问得好。UDP在传输层确实没有粘包,但应用层的粘包是真实存在的。举个例子:

假设你设计了一个协议,每条消息前面加4字节的长度字段。发送端连续发了三条消息:

  • 消息1:长度100,内容100字节
  • 消息2:长度200,内容200字节
  • 消息3:长度50,内容50字节

接收端一次recvfrom拿到了350字节。这时候你怎么办?你得自己解析:先读4字节长度,再读100字节内容,再读4字节长度,再读200字节内容……这个过程,本质上就是在处理「应用层的粘包」。

所以,UDP的粘包问题不是传输层的问题,是应用层协议设计的问题。这也是咱们这门课要重点讨论的内容。

我的建议:设计UDP应用层协议时,一定要考虑消息边界。常用的方案有三种:固定长度、长度前缀、特殊分隔符。后面几章我会详细讲每种方案的实现和坑。

好了,UDP的基础就复习到这儿。记住一句话:UDP无连接、不可靠、有边界。这个「有边界」既是它的优势,也是应用层协议设计时需要特别关注的地方。