【Linux】UDP协议详解:无连接、不可靠但高效的传输协议

文章目录

UDP协议详解:无连接、不可靠但高效的传输协议

💬 开篇:前面我们学习了HTTP、HTTPS等应用层协议,这些协议都运行在传输层之上。传输层负责把数据从"本机某个进程"送到"对端某个进程"(靠端口号复用/分用)。传输层有两个主要协议:TCP和UDP。TCP复杂但可靠,UDP简单但高效。这一篇先从UDP讲起,理解UDP的设计哲学------放弃可靠性换取高效率。掌握UDP,你就理解了为什么有些应用选择UDP而不是TCP,比如视频直播、在线游戏、DNS查询。

👍 点赞、收藏与分享:这篇会把UDP的原理讲透,包括报文格式、特点、缓冲区机制、应用场景。如果对你有帮助,请点赞收藏!

🚀 循序渐进:从传输层的概念讲起,到端口号,到UDP报文格式,到UDP的特点,到缓冲区机制,到应用场景,一步步理解UDP为什么这样设计。


一、传输层的作用

1.1 网络分层回顾

还记得我们之前讲过的网络分层吗?

bash 复制代码
应用层:HTTP、HTTPS、FTP、SSH、DNS、SMTP...
传输层:TCP、UDP
网络层:IP
数据链路层:以太网、WiFi
物理层:光纤、电缆

传输层的位置:介于应用层和网络层之间。

传输层的职责:

  • 应用层的数据如何传输到对方的应用层
  • 不关心数据怎么在网络上跑(那是网络层的事)
  • 只关心数据能否可靠地到达对方

1.2 传输层的核心概念:端口号

问题:一台主机上可能运行多个应用程序,比如:

  • 浏览器(HTTP)
  • 邮件客户端(SMTP)
  • SSH客户端
  • 游戏

如何区分这些应用程序的数据?

答案:端口号(Port)。

端口号的作用:标识一个主机上进行通信的不同应用程序。

类比:

bash 复制代码
IP地址 = 城市地址(找到哪个城市)
端口号 = 房间号(找到城市里的哪个房间)

1.3 五元组:唯一标识一条通信

在TCP/IP协议中,一条通信由五个要素唯一标识:

bash 复制代码
(源IP, 源端口, 目的IP, 目的端口, 协议号)

例子:

bash 复制代码
(192.168.1.100, 54321, 8.8.8.8, 53, UDP)

这表示:

  • 从192.168.1.100的54321端口
  • 到8.8.8.8的53端口
  • 使用UDP协议

查看本机的通信五元组:

bash 复制代码
netstat -n

输出示例:

bash 复制代码
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 192.168.1.100:54321     8.8.8.8:443            ESTABLISHED
udp        0      0 192.168.1.100:53214     8.8.8.8:53             

1.4 端口号的范围划分

端口号范围:0 - 65535(16位无符号整数)

分类:

范围 名称 用途
0-1023 知名端口(Well-Known Port) 系统保留,常用服务
1024-65535 动态端口(Dynamic Port) 客户端程序、临时端口

常见知名端口:

服务 端口 协议
SSH 22 TCP
Telnet 23 TCP
FTP 21 TCP
HTTP 80 TCP
HTTPS 443 TCP
DNS 53 UDP/TCP
SMTP 25 TCP
POP3 110 TCP
MySQL 3306 TCP
Redis 6379 TCP

查看系统的知名端口:

bash 复制代码
cat /etc/services

重要:自己写程序时,要避开这些知名端口。

1.5 端口号的两个问题

问题1:一个进程能bind多个端口吗?

答案:可以。

例子:

cpp 复制代码
// 一个服务器程序同时监听多个端口
TcpServer server1("0.0.0.0", 8080);  // 监听8080
TcpServer server2("0.0.0.0", 8081);  // 监听8081
TcpServer server3("0.0.0.0", 8082);  // 监听8082

实际应用:

  • Nginx同时监听80和443端口
  • MySQL主从复制,主库监听3306,从库也监听3306
问题2:一个端口能被多个进程bind吗?

答案:不能(默认情况下)。

例子:

bash 复制代码
# 进程A监听8080
./server 8080

# 进程B尝试监听8080
./server 8080
# 错误:Address already in use

例外:某些情况下可以通过 socket 选项实现端口重用。

SO_REUSEADDR 常用于"端口快速重启复用"(避免 TIME_WAIT 等导致的占用)

Linux 下如果要"多个进程同时监听同一端口并做负载分担",通常用 SO_REUSEPORT


二、UDP协议概述

2.1 UDP是什么

UDP(User Datagram Protocol):用户数据报协议。

设计哲学:简单、快速、高效。

代价:不可靠。

类比:

bash 复制代码
TCP = 挂号信(可靠,但慢)
UDP = 明信片(快速,但可能丢失)

2.2 UDP报文格式

UDP报文结构:

bash 复制代码
┌─────────────────────────────────────────┐
│         UDP首部(8字节)                 │
├─────────────────────────────────────────┤
│  源端口号(16位)  │  目的端口号(16位)  │
├─────────────────────────────────────────┤
│  长度(16位)      │  校验和(16位)      │
├─────────────────────────────────────────┤
│         UDP数据(可变长度)              │
└─────────────────────────────────────────┘

字段说明:

  1. 源端口号(16位):发送方的端口
  2. 目的端口号(16位):接收方的端口
  3. 长度(16位):整个UDP报文的长度(首部+数据),最小8字节,最大65535字节
  4. 校验和(16位):用于差错检测(UDP不是 CRC,而是 16 位一补和),计算时还会包含 IP 的"伪首部",用于尽早发现报文在传输中被破坏或投递错误。
  5. 数据:应用层的数据

重要:UDP首部只有8字节,非常简洁。

2.3 UDP的特点

特点1:无连接

含义:不需要建立连接就可以直接发送数据。

对比TCP:

bash 复制代码
TCP:
1. connect(建立连接)
2. send/recv(传输数据)
3. close(关闭连接)

UDP:
1. sendto(直接发送)

优点:

  • 速度快(省去握手过程)
  • 开销小(不需要维护连接状态)

缺点:

  • 无法确认对方是否存在
  • 无法确认对方是否收到
特点2:不可靠

含义:不保证数据一定能到达对方。

可能发生的情况:

  • 数据丢失(网络拥塞、路由器故障)
  • 数据损坏(传输过程中出错)
  • 数据乱序(不同路径到达)

UDP的态度:

bash 复制代码
"我尽力了,但不保证。"

对比TCP:

bash 复制代码
TCP:
- 数据丢失 → 重传
- 数据损坏 → 丢弃并重传
- 数据乱序 → 重新排序

UDP:
- 数据丢失 → 不管
- 数据损坏 → 丢弃
- 数据乱序 → 不管

应用层的责任:

bash 复制代码
如果应用层需要可靠性,就自己实现。
特点3:面向数据报

含义:UDP保留应用层数据的边界。

具体表现:

  • 应用层发送多少数据,UDP就发送多少
  • 不会拆分数据
  • 不会合并数据

例子:

cpp 复制代码
// 发送端
char data[100] = "hello";
sendto(sock, data, 100, 0, ...);  // 发送100字节

// 接收端
char buf[100];
recvfrom(sock, buf, 100, 0, ...);  // 接收100字节
// 必须一次接收100字节,不能分多次接收

UDP 保留报文边界:一次 recvfrom 只能读到一个完整的 UDP 报文。

如果接收缓冲区 buf 比报文小,报文会被截断,多出来的部分直接丢弃(不会像 TCP 那样下次还能继续读)。
对比TCP:

bash 复制代码
TCP是面向字节流的,可以分多次读写。
UDP是面向数据报的,必须一次读写完整的报文。

重要:这是UDP和TCP最大的区别之一。


三、UDP缓冲区机制

3.1 UDP没有真正的发送缓冲区

TCP的发送缓冲区:

bash 复制代码
应用层 → 发送缓冲区 → 网络层 → 网络

TCP会把数据先放在缓冲区,然后逐步发送。

UDP的发送过程:

bash 复制代码
应用层 → 直接交给网络层 → 网络

UDP调用sendto后,数据直接交给内核的网络层,不经过缓冲区。

含义:

  • sendto返回成功 ≠ 数据一定能到达对方
  • sendto返回成功只表示数据交给了网络层

3.2 UDP有接收缓冲区

接收缓冲区的作用:

  • 存储从网络接收到的UDP报文
  • 应用层通过recvfrom从缓冲区读取

接收缓冲区的特点:

  1. 不保证顺序:
bash 复制代码
发送:报文1 → 报文2 → 报文3
接收缓冲区可能是:报文2 → 报文1 → 报文3
  1. 容量有限:
bash 复制代码
如果缓冲区满了,新到达的报文会被丢弃。
  1. 报文边界清晰:
bash 复制代码
每个报文都是独立的,不会混在一起。

3.3 UDP的全双工特性

全双工(Full Duplex):一个连接既可以读,也可以写。

UDP socket的特点:

cpp 复制代码
// 同一个socket既可以发送,也可以接收
sendto(sock, ...);   // 发送
recvfrom(sock, ...); // 接收

对比TCP:

bash 复制代码
TCP也是全双工的,但需要先建立连接。
UDP不需要连接,天生就是全双工的。

四、UDP的使用注意事项

4.1 UDP报文大小限制

UDP报文的最大长度:65535字节(16位长度字段)

实际限制:

bash 复制代码
理论最大值:65535字节
UDP首部:8字节
IP首部:20字节
实际数据:65535 - 8 - 20 = 65507字节

但还有更严格的限制:

MTU(Maximum Transmission Unit):最大传输单元。

bash 复制代码
以太网的MTU:1500字节
WiFi的MTU:1500字节

实际可传输的UDP数据:

bash 复制代码
1500 - 20(IP首部) - 8(UDP首部) = 1472字节

超过MTU的处理:

bash 复制代码
如果UDP报文超过MTU,IP层会进行分片。
分片后,如果任何一片丢失,整个报文就无法重组。

建议:

bash 复制代码
UDP报文大小不要超过1472字节。
如果需要传输更大的数据,在应用层手动分包。

4.2 UDP的校验和

校验和的作用:检测报文是否损坏。

如果校验和出错:

bash 复制代码
UDP直接丢弃该报文,不通知应用层。

应用层的感受:

bash 复制代码
"咦,这个报文怎么没了?"

4.3 UDP的应用层协议设计

由于UDP的不可靠性,应用层需要自己处理:

  1. 数据完整性:
bash 复制代码
添加校验码或哈希值
  1. 数据顺序:
bash 复制代码
添加序列号
  1. 数据可靠性:
bash 复制代码
如果需要可靠性,自己实现重传机制
  1. 数据边界:
bash 复制代码
由于UDP是面向数据报的,这个不用担心

五、基于UDP的应用层协议

5.1 为什么选择UDP

UDP的优势:

  • 速度快(无连接开销)
  • 延迟低(直接发送)
  • 开销小(首部只有8字节)

适用场景:

  • 实时性要求高
  • 对可靠性要求不高
  • 需要广播/多播

5.2 常见的UDP应用

DNS(Domain Name System)

功能:域名解析。

为什么用UDP:

  • 查询通常很小(一个域名)
  • 响应也很小(一个IP地址)
  • 实时性要求高
  • 如果丢失,客户端会重新查询

例子:

bash 复制代码
# DNS查询
nslookup www.google.com

# 使用UDP的53端口
DHCP(Dynamic Host Configuration Protocol)

功能:动态分配IP地址。

为什么用UDP:

  • 客户端还没有IP地址,无法用TCP
  • 需要广播(UDP支持广播)
NTP(Network Time Protocol)

功能:网络时间同步。

为什么用UDP:

  • 实时性要求高
  • 数据量小
视频直播、在线游戏

为什么用UDP:

  • 实时性要求极高
  • 丢失几帧不影响用户体验
  • 不需要完全可靠

例子:

bash 复制代码
直播:丢失一帧视频,用户看不出来
游戏:丢失一个位置更新,下一帧会纠正
TFTP(Trivial File Transfer Protocol)

功能:简单文件传输。

为什么用UDP:

  • 简单(不需要TCP的复杂机制)
  • 用于无盘设备启动

5.3 自定义UDP应用层协议

当你写UDP程序时:

cpp 复制代码
// 定义自己的协议
struct Request {
    uint32_t id;           // 请求ID
    uint32_t seq;          // 序列号
    char data[1024];       // 数据
};

struct Response {
    uint32_t id;           // 响应ID
    uint32_t seq;          // 序列号
    uint32_t status;       // 状态码
    char data[1024];       // 数据
};

// 发送请求
Request req;
req.id = 1;
req.seq = 1;
strcpy(req.data, "hello");
sendto(sock, &req, sizeof(req), 0, ...);

// 接收响应
Response resp;
recvfrom(sock, &resp, sizeof(resp), 0, ...);

六、UDP vs TCP快速对比

6.1 对比表

特性 UDP TCP
连接 无连接 面向连接
可靠性 不可靠 可靠
顺序 不保证 保证顺序
速度 快 慢
首部大小 8字节 20-60字节
数据格式 数据报 字节流
流量控制 无 有
拥塞控制 无 有
广播/多播 支持 不支持

6.2 选择标准

选择UDP:

  • 实时性要求高(视频、游戏、VoIP)
  • 对可靠性要求不高
  • 需要广播/多播
  • 数据量小

选择TCP:

  • 需要可靠传输(文件传输、邮件)
  • 需要有序到达
  • 需要流量控制
  • 需要连接管理

七、本篇总结

7.1 核心要点

传输层的作用:

  • 负责数据从发送端传输到接收端
  • 通过端口号区分不同的应用程序
  • 五元组唯一标识一条通信

端口号:

  • 0-1023:知名端口(系统保留)
  • 1024-65535:动态端口(客户端使用)

UDP的特点:

  • 无连接:直接发送,不需要握手
  • 不可靠:不保证到达、顺序、完整性
  • 面向数据报:保留应用层数据边界

UDP缓冲区:

  • 无真正的发送缓冲区(直接交给网络层)
  • 有接收缓冲区(存储接收的报文)
  • 接收缓冲区满了会丢弃新报文

UDP报文大小:

  • 理论最大:65535字节
  • 实际限制:1472字节(考虑MTU)
  • 超过MTU会被分片,任何一片丢失都会导致整个报文无法重组

UDP应用:

  • DNS、DHCP、NTP、视频直播、在线游戏
  • 实时性要求高的场景

7.2 容易混淆的点

  1. UDP没有发送缓冲区:

    • sendto返回成功 ≠ 数据一定能到达
    • 只表示数据交给了网络层
  2. UDP的不可靠性:

    • 不是UDP的bug,而是设计选择
    • 为了换取速度和低延迟
  3. UDP的面向数据报:

    • 和TCP的面向字节流不同
    • 应用层发多少,UDP就发多少
  4. UDP报文大小限制:

    • 不是65535字节就能发
    • 实际受MTU限制,通常1472字节
  5. UDP的全双工:

    • 同一个socket既可以发也可以收
    • 不需要建立连接

💬 总结:UDP是一个简单、快速、高效的协议。它放弃了可靠性,换取了速度和低延迟。理解UDP,就理解了为什么有些应用选择UDP而不是TCP。在实时性要求高的场景(视频直播、在线游戏、VoIP),UDP是最佳选择。但如果需要可靠传输(文件下载、邮件、网页),就必须用TCP。两者各有优缺点,没有绝对的好坏,只有是否适合。
👍 点赞、收藏与分享:如果这篇帮你理解了UDP的原理和特点,请点赞收藏!下一篇我们讲TCP,看看TCP如何通过复杂的机制来保证可靠性。

相关推荐
古法老登瞎折腾6 小时前
大数据智能运维实战(十一):运维 MCP 的工程化——权限、限流、审计与错误处理
java·大数据·运维·spring ai·mcp
hhhjhl7 小时前
Protobuf.js 跨端三方库仓颉适配全流程
javascript·网络·udp
琥珀.7 小时前
mysql中json数组转列表查询
linux·运维·服务器
dawnsky.liu7 小时前
在 RHEL 中使用 goose 实现远程主机管理
linux·运维·ai
ITxiaobing20238 小时前
中国公司海外广告投放的 IP 地址库选型:从“查归属”到“验真实”的工程视角
网络·网络协议·tcp/ip
KKKlucifer8 小时前
运营商非结构化文档安全管控体系建设实践
大数据·网络·安全
xiaoye-duck8 小时前
《C++ 工具库实战》gflags 命令行参数解析库详解
c++·第三方库
狂奔的sherry9 小时前
C++ 宏生成访问函数的代价:访问权限、调试和类型检查
c++·宏·编译·预处理器·类型检查
Linux运维技术栈9 小时前
CVE-2026-91403 高危漏洞修复:Rocky Linux 9 多版本 JDK 环境清理实操
java·linux·运维
云边云科技_云网融合10 小时前
连锁门店组网怎么做?SD-WAN 多分支组网的架构选型、部署与成本结构
服务器·数据库·php