再谈UDP协议

一、UDP协议格式

16位源端口号:发送端端口,用于回发响应;无需回复时可置 0。

16位目的端口号:接收端端口,操作系统据此分发数据给对应进程。

16位UDP长度:UDP 数据报总字节数(首部+数据)。最小 8,最大 65535(2ⁿ - 1)。实际数据部分最大 65507 字节(因 IP 首部至少 20 字节)。

16位UDP检验和:端到端差错校验。计算结果若为 0 则发送时置为全 1;收到为 0 表示未校验(极少见)。如果校验和出错,该报文会直接丢弃

Linux内核自带UDP协议,不需要额外安装配置。 在调用 socket(AF_INET, SOCK_DGRAM, 0) 时,内核就会自动启用 UDP 协议的支持。

数据的发送、接收、端口管理、校验和计算 等所有底层工作,都由内核中的 UDP 协议栈自动完成。我们只需要通过 sendto / recvfrom 等系统调用使用它即可。

二、UDP协议特点

1. 无连接

  • 知道对方的 IP 和端口后,直接发送数据,不需要像 TCP 那样经历三次握手。

  • 类比:寄信 -- 写好地址扔进邮筒,不需要提前和对方打招呼。

2. 不可靠

  • 没有确认机制,没有重传机制。

  • 即使网络故障导致数据包丢失,UDP 协议层也不会通知应用程序。

  • 注意:UDP 只提供可选的校验和,能检测但不能修复错误 -- 损坏或错误的数据包直接被丢弃。

3. 面向数据报(保留消息边界)

  • 应用程序一次 sendto 多少字节,对方必须用一次 recvfrom 完整读取同样字节的数据(若接收缓冲区不足,则截断且剩余数据丢失)。

  • 不会像 TCP 那样拆分或合并数据 -- 因此 不会粘包

三、UDP缓冲区

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

  • 当应用程序调用 sendto() 时,内核会:

    1. 将数据从用户空间拷贝到内核空间

    2. 立即封装成 UDP 数据报(添加 UDP 头部)。

    3. 直接交给 IP 层,进行后续路由、分片(如果需要)、发送。

  • 内核不会 为这个数据报保留一份拷贝(不像 TCP 需要保留以应对丢包重传)。因此通常说:UDP 没有发送缓冲区

  • 如果 IP 层或网卡队列暂时繁忙,数据包可能在 IP 层或驱动队列短暂排队,但这不是 UDP 层独立维护的发送缓冲区,而是内核数据结构

类比:寄信时,你把信交给邮局窗口,邮局直接分拣发走,不会为你保留副本。

接收缓冲区:UDP 有独立的接收缓冲区

每个 UDP socket 在内核中都有一个接收缓冲区(实际上是一个队列,存放收到的 UDP 数据报)。

工作流程:
  1. 网卡收到 UDP 数据报,经过 IP 层解包,确认协议为 UDP。

  2. 内核根据目的端口号找到对应的 UDP socket。

  3. 内核将整个 UDP 数据报(包括数据部分)拷贝到该 socket 的接收队列末尾。

  4. 应用程序调用 recvfrom() 时,内核从队列头部取出一个完整的数据报拷贝给用户。

关键特性:
  • 每个数据报独立存储 :队列中的元素是完整的 UDP 数据报(应用程序一次 sendto 对应一个队列元素)。

  • 不合并 / 不拆分 :接收方必须用一次 recvfrom 读取一个完整数据报;若用户缓冲区小于数据报大小,多余数据被丢弃 (且返回 MSG_TRUNC 错误)。

  • 顺序不保证:网络传输可能导致乱序,队列按到达顺序存放(即乱序到达则乱序交付)。

  • 满则丢 :如果队列已满,新到达的 UDP 数据报被直接丢弃,不通知发送方。

四、细节理解

报文

在操作系统内核里,一个网络报文并不是一个简单的连续内存块,而是由两部分组成:

  1. struct sk_buff 结构体:报文的 "管理元数据",记录报文的所有信息(长度、协议类型、指针等)。
  2. 线性数据缓冲区:报文实际存储的字节数据,包括各层协议头和应用数据。

sk_buff 里的这几个指针是核心:

复制代码
struct sk_buff {
    unsigned char *head;  // 缓冲区起始地址
    unsigned char *data;  // 当前协议层的起始地址
    unsigned char *tail;  // 当前协议层的结束地址
    unsigned char *end;   // 缓冲区结束地址
    // ... 其他字段
};

封装过程(发送方)

  1. 应用层 :内核分配 sk_buffdata 指向缓冲区中部(预留头部空间),拷贝用户数据到当前位置。

  2. UDP 层data 向前移动 8 字节,填入 UDP 头。

  3. IP 层data 再向前移动 IP 头长度(20 字节),填入 IP 头。

  4. 链路层data 再向前移动以太网头长度(14 字节),填入 MAC 头。

最终 data 指向帧头,tail 指向数据尾,交给网卡发送。

总结 :逐层向左移动 data 指针,在空出的位置填头部,无需移动数据本身。

解包过程(接收方)

  1. 链路层 :网卡收到帧,data 指向帧头。识别类型后,data 向右移动 14 字节 → 指向 IP 头。

  2. IP 层 :校验后,data 向右移动 IP 头长度 → 指向 UDP 头。

  3. UDP 层 :校验后,data 向右移动 8 字节 → 指向应用层数据。

  4. 应用层 :内核将 datatail 之间的数据拷贝给用户。

总结 :逐层向右移动 data 指针,跳过各层头部,最后剩下的就是应用数据。

封装和解包最核心的操作就是移动指针。

读数据到应用层

socket 作为文件描述符,其读操作本质上就是从内核中该 socket 的接收队列里取数据。当网卡收到 UDP 报文后,内核通过逐层解包(移动 sk_buffdata 指针)剥离掉以太网头、IP 头和 UDP 头,使 data 指针最终指向纯净的应用层数据,随后将这个 sk_buff 挂入对应 UDP socket 的接收队列。

当用户程序调用 readrecvfrom 时,内核只是简单地将队列中已准备好的应用层数据从内核空间拷贝到用户空间缓冲区,并释放或回收相应的 sk_buff

相关推荐
apcipot_rain2 分钟前
计科八股20260529——连接协议连接线程池、模块拆解模块通信、WebSocket
运维·服务器·网络·八股
TechWayfarer29 分钟前
IP精准定位服务在快递网点规划中的应用:如何用客户位置数据辅助选址
大数据·网络·python·tcp/ip·交通物流
leduo668899o30 分钟前
知识付费系统深度测评:7款平台,内容加密+视频水印功能实测对比
大数据·网络·音视频
Geometry Fu42 分钟前
《物联网安全》第4章 网络攻防实例
网络·物联网·安全·网络攻击·网络攻防
德迅云安全-小潘1 小时前
数字化浪潮下,企业如何选型云场景DDoS防护方案?
网络
阿文的代码库1 小时前
用于事件驱动系统的WebSocket
网络·websocket·网络协议
数字护盾(和中)1 小时前
攻击链识别:企业抵御快攻型勒索攻击的关键能力
网络·安全·web安全
志栋智能1 小时前
超自动化运维:如何降低人为错误?
大数据·运维·网络·人工智能·自动化
都市放羊2 小时前
网络小白自学网工——因特网与网络互联技术
网络·笔记·自学
不只会拍照的程序猿2 小时前
深入理解AFDX(ARINC 664 Part7):从原理到实现(上篇)
网络协议·航空总线·afdx·arinc 664