TCP 用 20 字节的头把"可靠"做成全套服务,UDP 只留 8 字节,做一件事:把数据交给正确的应用程序。它不建连接、不确认、不重传、不排序、不控速——不是做不到,是故意不做。代价是丢包乱序全归应用自己管;回报是极低的延迟与开销,外加一项 TCP 永远给不了的能力:广播和组播。标题里那个"只"字,就是它的全部人设。
3.3 只在 TCP/UDP 对比表里给 UDP 留了一格"同城闪送",讲的是怎么选。 本节是 UDP 的正面特写——它的头长什么样、为什么这么薄、砍掉了什么、这些取舍在真实世界里怎么用。
一、先正名:UDP 是谁
- 全称 User Datagram Protocol,用户数据报协议
- 用词注意:UDP 的产物叫 数据报(Datagram),不叫数据段(Segment)——"数据段"是 TCP 专用的名字(收 3.6 的旧账)
- 装在 IP 包里时,协议号是 17(收 3.7 那张协议字段表:
6 = TCP、17 = UDP、1 = ICMP)
定位一句话:
UDP = IP + 端口 + 校验和。
它几乎是"把 IP 的能力原样端给应用程序",只在上面薄薄加了两样东西:
| 加的东西 | 干什么 |
|---|---|
| 端口 | 让数据找到正确的程序——这就是标题说的"分发" |
| 校验和 | 让明显损坏的数据别被交上去 |
除了这两样,它什么都没做。这既是它被嫌弃的地方,也是它被需要的地方。
二、首部只有 8 字节(本节第一张图)
四行两列,一共就四个字段:
| 字段 | 位宽 | 干什么 |
|---|---|---|
| 源端口 | 16 位 | 谁发的(不用回信时可以填 0 省略) |
| 目的端口 | 16 位 | 交给哪个程序——"分发"全靠它 |
| 长度 | 16 位 | UDP 头 + 数据的总长(最小 8,最大 65535) |
| 校验和 | 16 位 | 检验数据有没有损坏 |
对比 3.18 那 20 字节:UDP 砍掉了 序号、确认号、窗口、标志位。
回看 3.18 的一句话:那四个字段就是"可靠"的全部零件。
UDP 一个都不留——所以它不可能做到可靠、不可能做到排序、不可能做到流量控制。这不是"简化",是从根上不做。
3.18 特别指出过:TCP 头里没有"总长度"字段,因为它外面套了 IP 包,可以从 IP 头的总长度倒推。
那 UDP 为什么必须自己带一个长度?
因为 IP 头里的总长度是整个 IP 包的长度,还包含了 IP 头自己;更麻烦的是——一旦发生分片,每个分片都会带上一个 IP 头,它的"总长度"只是那个分片的长度,靠它根本算不出原来的 UDP 数据报有多长。
也就是说:IP 的长度字段没法替 UDP 表达"我这个数据报有多大"。这个字段,是"数据报有边界"的第一层证据。
UDP 的校验和覆盖 伪首部(源 IP + 目的 IP + 协议号 + UDP 长度)+ UDP 头 + 数据——它连"这个包是不是送错机器了"都在查。为什么?因为如果包被投递到错误的机器,而那里恰好有程序守着同一个端口号,就出大乱子了。
但它是只查不修:校验不过就悄悄丢掉,谁也不通知。这和 TCP 完全不同——TCP 也会丢,但丢了会靠重传补回来。
两个细节:IPv4 里 UDP 校验和可以关掉(填 0 表示不算);IPv6 里是强制的,不许关。
三、四个"不管",就是"不可靠"的全部含义
| 不管什么 | 表现 | 后果 |
|---|---|---|
| 不建连接 | 没有握手,拿起来就发 | 想发就发,但对方在不在完全不知道 |
| 不确认、不重传 | 发出去就忘 | 丢了就丢了,发送方不会知道 |
| 不排序 | 每个数据报独立走路 | 后发的可能先到(收 1.4 的逐跳接力:每一跳都可能出岔子) |
| 不控速 | 没有窗口(收 3.18),没有拥塞控制 | 接收方缓冲区满了直接丢;发送方也不会自己收敛 |
一句话:
TCP 把网络的不确定性替应用挡掉了;UDP 把不确定性原样交给应用。
别把 UDP 当成"简化版/劣质版 TCP"。它面向的是另一类需求——那些丢掉一小块也完全不影响使用的数据。
视频通话里丢一帧:画面糊 1/30 秒,人根本察觉不到。 但如果为这一帧重传:画面卡住等一下,后面的帧也跟着延迟——这时候"补"是负作用。
所以 UDP 的逻辑不是"我做不到",而是"这类数据不需要,做了反而添乱"。
四、数据报 vs 字节流:最本质的差别(本节第二张图)
同样的动作交给两个协议,接收方拿到的东西根本不一样。
| TCP | UDP | |
|---|---|---|
| 交给应用的是什么 | 一条连续的字节流 | 一个一个独立的数据报 |
| 边界 | 没有(你写 3 次,对面可能一次全读完) | 保留(一次 receive 正好收到一个完整数据报) |
| 少写一次会怎样 | 可能读到两次 write 的内容粘在一起 | 不可能——没发就没有 |
| 一句口诀 | 字节流没有消息边界 | 数据报边界天然保留 |
第二张图画的就是这件事:同样由程序写三次 A、B、C——TCP 那边过来是连成一片的 ABC(这就是 3.18 那个"粘包"),UDP 那边过来是三个各自完整的包裹。
收 Java 13.3 的四笔账
这一节正好把 UDP通信 里那几条当时只说了"就这么写"的规矩,全部补上原因:
| Java 13.3 的规矩 | 协议层的原因 |
|---|---|
| "地址在包裹上,不在连线上" | 无连接的工程体现。TCP 的地址定在"连线"上(new Socket(ip, 端口)),UDP 的地址贴在每一个包裹上(DatagramPacket 构造时要传 IP 和端口) |
| "两边用同一个类,没有服务端客户端之分" | 也是无连接的后果。TCP 的角色分工来自"谁开门谁敲门",UDP 谁都能寄、谁都能收(接收方只是指定了端口) |
| "不报错 ≠ 发到了" | 这就是不可靠的定义 |
| "整进整出,箱子小了多出来的直接丢" | 因为收发单位是"数据报",一个数据报是一整个整体——不可能像字节流那样"分两次装" |
严格说,UDP 协议本身不报错。但 ICMP 会替它报一部分错——如果接收方主机活着、只是那个端口没人守,对方会回一条 ICMP 类型 3 / 代码 3「端口不可达」(收 3.15)。
这正是 3.15 那句"UDP 唯一的反馈来自 ICMP"的另一半含义。编程层面通常把这条消息静默处理掉了,所以你"看不到",但它在网络上确实存在。
五、UDP 的五个优点(不只是"快")
很多人以为 UDP 的优点只有一个"快"。其实"快"是结果,根子在这五条:
- 没有握手延迟 — TCP 要先三次握手(约 1.5 个往返)才能发第一个字节,UDP 直接就发。DNS 查询是典型:一次一问一答,全靠 UDP 才能做到毫秒级
- 头小 — 8 字节 vs 20 字节。小包场景(游戏、语音,每包几十字节)里,这 12 字节的差距在开销占比上很可观
- 不维护连接状态 — 服务器不用为每个客户端记一份状态表。同样扛十万并发,TCP 要维护十万份连接状态,UDP 什么都不用存
- 能广播、能组播(收 3.9)— 这是 TCP 根本做不到的
- 应用层可以自己定制可靠性 — 把 UDP 当"毛坯房",想要什么自己搭。HTTP/3(QUIC)就是这么干的
TCP 只能单播,UDP 才能广播/组播。
这不是"哪个更好"的问题,而是能力有无的问题:一个 TCP 连接只能连一台机器,"用 TCP 做局域网设备发现"从根上就不成立。
所以 3.9 讲的广播与组播,在传输层找到的唯一合法出口就是 UDP。这也顺手解释了为什么不长的一段话里要反复出现 UDP:DHCP 的 DORA 四步(收 3.12)全是广播,而凡是广播,传输层只可能是 UDP。
六、四个代价(别只看好处)
- 丢包、乱序全归应用自己管 — 要么认(音视频),要么自己补(应用层编号、超时、重传)
- 没有 MSS 协商,大包会被 IP 分片(收 3.19 + 3.4)— TCP 靠 MSS 在源头切好、让 IP 永不分片;UDP 没有这个机制,应用塞多大就发多大,一旦超过 MTU 就触发 IP 分片,而丢任一片整包报废
- 没有拥塞控制 → 会挤占带宽 — 这是 UDP 被运营商限速、被某些网络封禁的原因;也是"用 UDP 打游戏很流畅、用 UDP 传文件却把全家网速拖垮"的根源
- 源地址可以随便伪造 → 会被拿来做攻击 — UDP 不用握手,攻击者可以伪造源 IP 发大量请求,让服务器把响应全砸向受害者(放大攻击 / 反射攻击,DNS、NTP、Memcached 都是著名弹药)。这是"无连接"带来的安全副作用
"UDP 一次能发 64KB"——65507 字节只是理论上限(65535 − IP 头 20 − UDP 头 8),而且它只是"IP 包的上限",不是建议值。
超过 MTU(1500)就得分片,丢一片整个数据报报废——而 UDP 不重传,就是彻底没了。
实用建议:单次发送控制在 1400 字节左右(给头留出安全余量),要传大的自己在应用层切。这就是 13.3 那句"实际建议远小于它"的完整原因。
七、选型:什么时候用哪个
| 场景 | 选谁 | 为什么 |
|---|---|---|
| 网页、文件、邮件、数据库 | TCP | 一个字节都不能少 |
| 直播、视频通话、在线游戏 | UDP | 丢一帧无感,延迟才是命 |
| DNS 查询 | UDP(响应大时转 TCP) | 一问一答、短小,省掉握手(收 3.17 的 53/UDP) |
| DHCP | UDP | 一开始还没有 IP,只能广播(收 3.12) |
| SNMP、TFTP、日志上报 | UDP | 短小高频,丢一条无所谓 |
| 视频会议、IPTV、设备发现 | UDP | 必须用组播(收 3.9) |
| HTTP/3 | UDP | 应用层自己实现可靠传输(QUIC) |
口诀沿用 13.3 的原话:丢得起选 UDP,丢不起选 TCP。 再补一句更实用的判据——看"延迟"和"完整"哪个更要命。
八、动手 Demo34:把 UDP 看个实在
-
看本机 UDP 账本
netstat -s -p udp找"收到的数据报""发出的数据报""接收错误"等项。刷新几次网页再看,数字会涨。 -
看本机在守哪些 UDP 端口(信息量很大)
netstat -ano -p udp常见的几条:123(系统时间同步 NTP)、137/138(NetBIOS 名称服务)、1900(SSDP 即插即用设备发现)、5353(mDNS)、5355(LLMNR 名称解析)、3702(WSD 设备发现)——这些全是 UDP。
为什么不用 TCP?因为它们要么需要广播/组播,要么只是简短的一问一答。
顺手验证一件事 你在这份清单里一般看不到
53——因为 DNS 客户端是发起方,用的是临时端口;只有本机自己当 DNS 服务器时才会守53。这正好复习 3.17 的"服务器守固定号,客户端借临时号"。
-
亲眼看一次 DNS 查询(本节最直接的一步)
nslookup www.baidu.com这一问一答默认走 UDP 53。留意它有多快——如果每次 DNS 查询都要先三次握手、再四次挥手,上网体验会明显变慢。这就是 UDP 在真实世界的价值。 -
用 Java 13.3 的 Demo24 做正反对照(收 13.3 的账)
把 13.3 的 Demo24Sender 改两行,连续 send 两个包裹:
- UDP 这边:接收方改成 while(true) 循环,会收到两个各自完整的数据报——边界清清楚楚
- TCP 那边(3.18 的粘包实验):连写两次,一次 read 可能读到两个拼在一起
同一个动作,两种协议给出来的结果完全不同——这组对照比任何文字都清楚。
- 30 秒实验:数一数 UDP 的"一次问答"
netstat -s -p udp记下"发出的数据报"数字 → 执行nslookup www.qq.com→ 再看数字。
大概只涨了个位数(一次查询,或许加一次重试)。对比 3.19 那个实验——访问一个网页要几十上百个 TCP 报文段。开销差距一眼可见。
- UDP 一共做几件事?标题里那个"只"字指的是什么?
- UDP 首部几个字节?四个字段分别是什么?
- UDP 头里有"长度"字段,TCP 头里没有——为什么?
- 为什么说 UDP 的"不可靠"是设计选择而不是缺陷?
- 同样由程序写三次数据,TCP 和 UDP 交给对方的东西有什么本质不同?
- UDP 有哪一件事是 TCP 根本做不到的?
- UDP 单包建议控制在多大?为什么不能发满 64KB?
- UDP 丢了包,发送方真的一点消息都收不到吗?
答案: ① 一件事:把数据交给正确的应用程序(靠目的端口号)。"只"字就是指它除了分发之外,什么可靠性、排序、流量控制一概不管。 ② 8 字节:源端口、目的端口、长度、校验和(各 16 位)。 ③ 因为 IP 头的总长度包含 IP 头自己,而且分片后每个分片的 IP 头长度只是该分片的长度——算不出原始 UDP 数据报有多长,所以 UDP 必须自带一个长度来界定边界。TCP 是字节流,不需要边界(3.18)。 ④ 因为它面向的是丢掉一小块也不影响使用的数据(音视频、游戏)。这类场景里,重传带来的延迟比丢包本身更伤——"补"是负作用。 ⑤ 边界:TCP 交过去的是一条连续字节流(原来分几次写的分不出来,会粘包);UDP 交过去的是一个一个独立数据报(一次 receive 收到一个完整包裹,边界天然保留)。 ⑥ 广播和组播。TCP 只能单播,一个连接只能连一台机器——所以凡是广播场景(DHCP、设备发现、IPTV),传输层只能是 UDP。 ⑦ 1400 字节左右。65507 只是理论上限(IP 包上限减两个头),超过 MTU 会被 IP 分片,丢一片整个数据报报废,而 UDP 不重传。 ⑧ 不完全是。UDP 自己不发任何差错消息,但 ICMP 会替它报——比如目标端口没人守时会回"端口不可达"(类型 3 / 代码 3,收 3.15)。编程层面通常静默处理,所以你"看不到"。
九、几条实战规矩
1. 用 UDP,就要自己把 TCP 干过的活补上
应用层至少要自己定这几样(每一样都是 TCP 免费提供的):
| 要补的 | 做法 |
|---|---|
| 消息编号 | 每个数据报里自己带一个序号字段 |
| 超时重传 | 自己设超时、自己补发(QUIC 就是这么干的) |
| 去重 | 收到重复序号就丢弃 |
| 排序 | 按自带序号在缓冲区里排好 |
| 包大小控制 | 自己切到 1400 字节以内 |
想清楚了这几样,才算真的会用 UDP。
2. 分清"卡"是延迟还是丢包
UDP 应用对丢包容忍、对延迟敏感。用户抱怨"卡"时先分清是哪一种:延迟高要优化路径和发包节奏,丢包高才要查链路质量。
3. 局域网设备发现,只能用 UDP 广播/组播
想写一个"自动找到同网段的其他设备"的功能,别用 TCP——一个 TCP 连接只能连一台,你不知道该连谁。用 UDP 广播(255.255.255.255 或定向广播)或者组播。
4. 别在生产环境拿 UDP 传大块数据
除非你自己在应用层做了完整的可靠传输(那就等于重写了半个 TCP)。大文件老老实实用 TCP(收 13.2)。
十、常见误区
- ❌ "UDP 是劣质版 TCP" → 面向不同需求的设计选择:一个保完整,一个保实时
- ❌ "UDP 一定比 TCP 快" → 不是"传输速度"快,是开销小、延迟低;没有拥塞控制反而可能把线路搞乱、被限速
- ❌ "UDP 没有校验" → 有校验和,而且连伪首部都查(能发现"送错机器");只是只查不修
- ❌ "UDP 也有连接" → 无连接。只有"对端地址"这个参数,没有连接状态、没有握手
- ❌ "广播/组播 TCP 也能做" → 只有 UDP 能做,这是能力的差别不是优劣的差别
- ❌ "UDP 和 TCP 一样是字节流" → 数据报有边界,整进整出;一次 receive 正好一个
- ❌ "一次能发 64KB" → 理论上限 65507,实用要远小于(建议 ≈1400)
- ❌ "UDP 丢包完全没人管" → 应用不管就真没了;但 ICMP 会替你报"端口不可达"这类错(收 3.15)
- ❌ "UDP 不用管乱序" → 协议层确实不管,但应用层自己要处理(每个数据报自带序号)
- ❌ "UDP 的端口和 TCP 是一回事" → 两套独立编号空间(收 3.17),
53/UDP≠53/TCP
十一、下一步学什么
- ☑ 3.21 网络的电话簿(域名解析、主机名、DNS) —— ✅ 已完成 → 网络的电话簿(域名解析、主机名、DNS):本节 Demo34 里那次
nslookup到底发生了什么,那一节完整展开。点此复习。
相关笔记
- 网络的电话簿(域名解析、主机名、DNS) —— 下一节:本节 Demo34 里
nslookup那一问一答,就是它讲的 DNS 查询;"为什么 DNS 能用 UDP"也在那里收尾 - 用TCP对数据进行分割处理(序列号、ACK号) —— 上一节:TCP 在源头主动切成段、还在握手时协商 MSS,本节讲的是"不切、也不协商"的那一半
- 对应用程序的数据进行可靠传输(TCP) —— 正面对照:本节砍掉的四个字段(序号、确认号、窗口、标志位)全是它搭建可靠传输用的零件;"粘包"也是它讲的
- 根据端口号分配数据到应用程序(端口号、已知端口号) —— 本节"分发"的凭据(端口)在那里讲的;
53/UDP与53/TCP是两扇门 - 数据是否成功传输给接收方(ICMP) —— UDP 唯一的反馈来源:端口没人守时,ICMP 会回"端口不可达"
- 连接网络的过程(IP地址的设置) —— DHCP 的 DORA 四步全程走 UDP 广播(67/68),是"UDP 支持广播"的经典应用
- 接收方是1个还是多个(单播、广播、组播) —— 广播与组播在传输层的唯一出口就是 UDP
- 数据的发送和接收(IP、IP数据包、路由) —— 协议号
17 = UDP的出处 - 数据的称谓(信息、数据段、数据包、数据帧) —— "UDP 的产物叫数据报,不叫数据段"这条规矩的出处
- 收发数据时的规则(数据打包) —— MTU 1500 与 IP 分片的出处;UDP 没有 MSS 协商,所以更容易踩到分片
- 为驱动应用程序而做准备的网络分层(传输层与应用层) —— TCP/UDP 对比表的最早出处
- UDP通信 —— "地址在包裹上""整进整出""不报错 ≠ 发到了"的代码出处,本节把这些规矩的协议层原因补全了
- TCP通信 —— 对照着看:TCP 那边要握手、要关流、要处理粘包,UDP 这边代码短一大截