用户数据报协议(UDP)详解

作者:CodePracticer日期:2025/11/28

一、传输层协议UDP

1. 理解UDP协议

我们以前说过,0-1023端口号是知名端口号,它们是与指定的协议进行关联的,那么我们如何证明呢?

在指定目录下就可以查找到这些协议的端口号了(/etc/services)

在这里插入图片描述
在这里插入图片描述

这里以两个例子来说明情况。

前面我们也说过协议就是一种约定,本质就是结构体。今天我们来正式认识一下UDP协议。

在这里插入图片描述

可以看到UDP协议的宽度是32位,源端口号和目的端口号分别占16位,UDP协议的报头是8字节

前面我们说过,源主机的数据发送给目标主机需要先经历封装在解包的过程,而在这个过程中需要面临两个问题,一是报头和有效载荷的分离,二是分用问题(即应该将有效载荷交给上层的哪一个协议进行处理)。

现在就可以回答这两个问题了。报头和有效载荷的分离是通过报头的长度解决的,UDP协议的报头固定是8个字节

那该如何解决分用问题呢?如何返回给对端呢?(假设目标向源主机返回)

是通过端口号实现的,UDP协议里带着源主机和目标主机的端口号呀,端口号唯一约定着关联的协议

2. 面向数据报

那为什么说UDP协议是面向数据报的呢

UDP协议里还有一个16位的UDP长度,该长度表明UDP协议的总长度总长度 - 8字节,剩下的就是正文部分的长度了,只需要比较正文长度和总长度 - 8 字节(即剩下的字节长度)是否相等即可

UDP的长度是发送端的UDP层填写的,应用层交给UDP多长的报文,UDP原样发送,既不会拆分也不会合并,假设发送端调用一次sendto,发送100个字节,那么接收端也必须调用一次recvfrom,接收100个字节,而不能循环调用recvfrom,这就叫做面向数据报

3. UDP的缓冲区

UDP没有真正的发送缓冲区,因为没必要,UDP协议不考虑可靠性,不考虑失败了重传等问题,调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作

UDP具有接收缓冲区,但是这个接收缓冲区不能保证接收的UDP报的顺序和发送UDP报的顺序一致如果缓冲区满了,再到达的UDP数据就会被丢弃,所以,下下一次到达的数据就有可能被接收,而上一次到达的数据会被丢弃,所以不能保证一致性

udp的socket既能读也能写,这个概念叫做全双工

UDP的注意事项:我们可以看到在UDP协议里有一个16位的UDP长度,代表着UDP协议的总长度,也就是说UDP协议一次性最多只能发送2^16次方大小的数据,那如果发送的数据比这个数值大呢?那就只能在应用层进行分包,多次发送了

4. 理解什么是报文

比如,主机A现在给主机B发送数据,该数据在应用层就要经历一次封装,即添加报头,形成一个新的报文,再将该报文传递给传输层,传输层添加自己的协议报头,形成一个新的报文,以此类推,对端再依次进行解包,可是主机A给主机B发送数据并不只是发送一次,而且在网络中也不仅仅只有这两台主机在通信,所以在OS中一定会有大量的报文,在不同的层,也一定会同时存在多个报文,那么OS要不要对这些报文进行管理呢?答案是要的,先描述在组织

所以,在OS内就会有描述报文的结构体,但是有一个新的问题,数据在网络中传输是需要经过网络协议栈的,也就是说在每一层都会有不同的报文,那我们要用结构体去描述报文,难道要给每一层都要描述一个报文的结构体吗?那这样岂不是太麻烦了,我们肯定是想要用一个结构体来描述所有报文的。报文也是有数据的,所以也是需要内存空间来存储报文的

现在大家对于报文已经有了一个概念了,但是大家还不能区分报文和报头的区别,我们就一起来了解一下。

在OS中描述报文的结构体叫做struct sk_buff,这个结构体里有几个成员

1struct sk_buff
2{
3	struct sk_buff* next;
4	struct sk_buff* prev;
5	sk_buff_data_t tail;
6	sk_buff_data_t end;
7	unsigned char* head,*data;
8}
9

可以看到,next,prev指针就表明了报文在OS中是以双链表的形式管理的,而head,end指针分别指向数据区的起始和末尾位置,这两个不作为重点。接着便是data,tail指针了,tail指针指向应用层数据的末尾位置,data指针刚开始指向应用层数据的起始位置,当应用层数据传递给传输层,添加传输层协议报头之前,就需要对data指针进行操作了,将data指针向上移动传输层协议报头的长度,就可以添加报头了,到达网络层也是一样的道理,继续向上移动网络层协议的报头长度,就为网络层协议报头留出了空间,这就是封装反之,解包就是封装的反过程,将data指针向下移动链路层协议的报头,剩下的就是有效载荷。这就是解包。之所以封装时指针向上移动,是因为默认上面的地址空间是低地址

网络报文,在系统中的存在,其实就是一个内存空间布局

可以结合下面这张图理解。

在这里插入图片描述

5. socket和文件系统的关系

网络通信的操作本质上也是一个进程发起的,是进程就有PCB,文件描述符,所以在struct file结构体里有一个void* private_data指针,该指针就指向struct socket结构体,这个结构体里有以下几个成员

1struct socket
2{
3	socket_state state;//描述网络通信的状态
4	const struct proto_ops* ops;//网络协议的操作
5	struct file* file;//回指向struct file
6	struct sock* sk;//指向struct sock结构体
7	wait_queue_head_t wait;//等待队列
8}
9

这就是为什么网络通信需要返回一个文件描述符的原因了通过file指针回指向file结构体,就可以通过private_data指针找到socket,从而进行网络通信,符合linux下一切皆文件的大一统思想,同时降低了学习者的成本,要不然还要再系统性的学习一套关于网络通信的接口。

struct socket结构体里还有一个指针struct sock* sk指针,该指针指向一个struct sock结构体

1struct sock
2{
3	struct sk_buff_head sk_receive_queue;//接收缓冲区
4	struct sk_buff_head sk_write_queue;//发送缓冲区
5}
6

这两个缓冲区的指针类型是struct sk_buff_head,这也是一个结构体

1struct sk_buff_head
2{
3	struct sk_buff* next;
4	struct sk_buff* prev;
5}
6

看到这里大家应该已经明白的差不多了。不过不知道大家有没有疑问呢?struct sk_buff_head结构体里有struct sk_buff* next,*prev指针,struct sk_buff结构体里也有这两个指针它们之间是怎么回事呢

前面我们已经说过,struct sk_buff结构体是用来描述报文的,而这两个指针是用来实现双链表管理报文队列的那struct sk_buff_head里的这两个指针是用来干什么的呢

这两个指针是用来分别指向struct sk_buff结构体,即第一个报文的节点,而prev指针是用来指向报文最后一个节点的,这两个指针管理着报文队列的生命周期。struct sk_buff和struct sk_buff_head结构体里的这四个指针一起共同管理着报文队列


用户数据报协议(UDP)详解》 是转载文章,点击查看原文


相关推荐


苹果ios手机ipad安装配置ish终端shell工具
无痕melody2025/12/9

简介 官方介绍 iSH 是一个运行在 iOS 上的 Linux Shell,用来在ARM架构的 iOS 设备上模拟 X86 架构。也就是说不光是 IPad 可以安装,IPhone 上也可以安装运行 iSH,直接在 IOS 设备上运行 Linux 环境,而且免费! 如果你正在使用的电脑是 Mac,那么可以把 iSH 比作你电脑上面的终端。 iSH 官方地址 安装 AppStore里搜索ish或手机打开链接 配置 基本操作 操作按钮 2. 这个按钮相当于电脑上的 Tab 键,用于命令


从已损坏的备份中拯救数据
神奇的程序员2025/12/17

前言 12月15号早上,一觉醒来,拿起手机看到我的邮箱收到了内网服务无法访问的告警邮件,本以为只是简单的服务卡死,将服务器重启后就去上班了。 后来,陆续有好友联系我说网站挂了。 定位问题 晚上下班回家后,尝试将电脑断电重启,发现pve只能存活2分钟左右,然后整个系统卡死,无法进行任何操作。首先,我想到的是:会不会某个vm虚拟机或者ct容器影响到宿主机了。 因为系统只能存活几分钟,在执行禁用操作的时候,强制重启了好几次服务器。当所有的服务都停止启动后,卡死的问题依旧存在。 翻日志 没辙了,这已经


Vue 实例挂载的过程是怎样的?
全栈陈序员2025/12/25

一、整体流程概览 当我们执行 new Vue({ ... }) 时,Vue 会经历 初始化 → 编译模板 → 挂载 DOM 三个阶段。整个过程由 _init 方法驱动,最终通过 $mount 完成视图渲染。 核心路径: new Vue() → _init() → initState() → $mount() → mountComponent() → _render() → _update() → 真实 DOM 二、详细步骤解析 1. 构造函数与 _init 初始化 源码位


Go 项目结构总是写乱?这个 50 行代码的 Demo 教你标准姿势
Java小成2026/1/4

1. 场景复现:那个让我头疼的时刻 去年,我接手了一个"祖传" Go 项目。打开代码仓库的那一刻,我整个人都不好了——所有代码都塞在一个 main.go 里,足足 3000 多行。想加个功能?先花半小时找代码在哪。想写个单元测试?抱歉,函数全是私有的,而且互相耦合,根本没法单独测。 我当时就在想:如果当初写这个项目的人,能从第一天就用一个规范的结构,后面的人得少掉多少头发? 后来我开始研究 Go 官方和社区推荐的项目布局,发现其实规则很简单,但很多人就是不知道。于是我写了这个 50 行代码的小


Ansible自动化(十五):加解密详解
cly12026/1/12

Ansible Vault 是 Ansible 提供的一套用于保护敏感数据的机制,可以对各类配置文件进行加密,防止敏感信息(如密码、私钥、API 密钥等)以明文形式暴露在代码仓库或配置文件中。 一、为什么需要 Ansible 加密? 场景说明: Playbook 中包含数据库密码、API Token、SSH 私钥等敏感信息Inventory(主机清单)中直接写入了连接密码(如 ansible_password)变量文件(vars/main.yml)中包含机密配置 ✅ Ansible Vaul


筑牢金融底座:企业级区块链全球化数据库架构设计白皮书
China_Yanhy2026/1/20

📖 前言:Web3 业务的双重账本 在 Web3 业务中,区块链(AMB)是不可篡改的“链上真理”,而关系型数据库(RDS/Aurora)则是承载用户资产、撮合逻辑和KYC信息的“链下业务核心”。对于追求全球化的高频交易项目,数据库的架构设计必须解决两个核心矛盾:跨国访问的物理延迟 与 资金数据的一致性。 第一部分:旗舰方案 —— Amazon Aurora Global Database (深度解析) 这是针对跨国交易所(如币安、Coinbase 模式)的首选架构。 1. 核心架构


Spring注解秘籍:优雅地使用 @RequestHeader
独泪了无痕2026/1/29

前言   在 Spring Boot 开发中,HTTP 请求头(Header)是客户端和服务器之间传递元数据的重要方式。通过请求头,客户端可以传递认证信息、内容类型、语言偏好等数据。Spring Boot 提供了 @RequestHeader 注解,用于方便地从 HTTP 请求头中提取数据。本文将详细介绍 @RequestHeader 注解的使用方法,包括基本用法、默认值处理、多值头处理以及实际应用场景。 一、注解定义与核心属性 1.1 @RequestHeader 是什么   在构建现代 W


Skills.lc 是什么?为什么我会做(用)这个站
HBLOG2026/2/7

在折腾 AI Agent、CLI 工具和各种自动化脚本的过程中,我一直有一个很现实的问题: 好的 skill / workflow 到底该放哪?怎么复用? Prompt 太零散,放在 Notion、Gist、README 里,时间一长就找不到; 不同项目里反复复制粘贴,又很难维护; 看到 GitHub 上有人写了不错的 skill,也不知道怎么发现、怎么用。 Skills.lc 就是在这样的背景下出现的。 它本质上不是“又一个 AI 平台”,而是一个 技能索引与分发站点,专门用来收集、整理


我又开发了一款桌面APP,功能强大
500佰2026/2/16

最近这段时间,开始沉迷一件事,在抖音录制我AI写代码、做实战开发的视频,用opencode / claudecode / Agent skills 等大模型进行AI项目开发,耗时7个晚上,最晚的一次,写到了夜间3点,录制了5个视频,开发消耗AI大模型token 数1500左右。 这次我开发了一款桌面录屏APP,名字叫做focusME,目前已经开发完成,可一键安装在我们的桌面,接下来讲解一下整个开发过程。 开发成果 开发过程 前面我用opencode里面Agent skills去制定产品


Django 应用 OOM(Out of Memory)故障的定位思路和排查方法
哈里谢顿2026/2/24

二、定位思路总览 1. 确认现象 → 2. 内存分析 → 3. 代码审查 → 4. 复现验证 → 5. 修复优化 ↑___________________________________________________________| 三、详细排查步骤 第一步:确认内存使用趋势 1.1 系统层面监控 # 查看进程内存(RSS:实际物理内存,VSZ:虚拟内存) ps aux --sort=-%mem | head -20 # 实时观察 watch -n 1 'ps -p <PID>

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客