Linux网络传输层协议UDP

目录

前言

[1. 前置知识回顾](#1. 前置知识回顾)

[2. UDP协议详解](#2. UDP协议详解)

UDP协议格式

UDP的特点

UDP的缓冲区

[基于 UDP 的应用层协议](#基于 UDP 的应用层协议)

[3. 对报文的理解](#3. 对报文的理解)

尾声


前言

ok,也是好久没复习过网络部分了,也好久没更新过网络部分的博客了,那么今天借着复习的机会更新一下这篇UDP协议的博客吧!

前面我们在socket编程部分学习过socket UDP协议代码的编写,但是我们还没有系统性的学习过UDP的理论,那么这一节就要去了解我们的传输层UDP协议的理论部分啦(本篇较为简单,但也为我们后面系统学习TCP时做了一些知识铺垫)

1. 前置知识回顾

先回顾一下传输层的工作:负责数据能够从发送端传输接收端的任务

再谈端口号:端口号(Port)标识了一个主机上进行通信的不同的应用程序

在 TCP/IP 协议中, 用 "源 IP", "源端口号", "目的 IP", "目的端口号", "协议号" 这样一个五元组来标识一个通信(可以通过 netstat -n 查看)

\^\] 协议号表示客户端选择的是UDP协议通信还是TCP协议通信 端口号范围划分 * 0 - 1023:知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议,他们的端口号都是固定的 * 1024 - 65535:操作系统动态分配的端口号. 客户端程序的端口号,就是由操作系统从这个范围分配的 认识知名端口号(Well-Know Port Number) 有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号: * ssh 服务器, 使用 22端口 * ftp 服务器, 使用 21端口 * telnet 服务器, 使用 23端口 * http 服务器, 使用 80端口 * https 服务器, 使用 443端口 执行下面的命令, 可以看到知名端口号 ```bash cat /etc/services ``` ![image-20250904115313116](https://i-blog.csdnimg.cn/img_convert/1d23abc9e4696e2c19ab5ace80b7fc37.png) **我们自己写一个程序使用端口号时, 要避开这些知名端口号** 两个问题 1. 一个进程是否可以 bind 多个端口号? 2. 一个端口号是否可以被多个进程 bind? 答:一个进程可以绑定多个端口号,但是一个端口号只能被一个进程绑定 ## **2. UDP协议详解** ### UDP协议格式 ![image-20250904120835935](https://i-blog.csdnimg.cn/img_convert/e5f9220a3daf598bd570a5149b51616f.png) > • 16 位 UDP 长度,为2\^16字节,也就是64KB大小, 表示整个数据报(UDP 首部+UDP 数据)的最大长度 > > • 如果校验和出错, 就会直接丢弃 两个问题: 1. 如何分离报头和有效载荷? 答:在上面的图中可以看到UDP协议报头是固定8字节的(定长报头),也就是说我们从整个UD协议报文中提取前8个字节就是报头,然后这个报头中有完整报文的长度信息,拿这个长度减去8字节就是有效载荷 2. 如何分用? 答:通过报头的目的端口号来确定有效载荷是要发往这个主机的哪个服务器的 UDP协议在内核中是结构体(协议本质),其8字节的报头结构体如下: ![image-20250904165950538](https://i-blog.csdnimg.cn/img_convert/099853a2105ef70652cd804f54befc77.png) 结论: 1. 因为内核协议是16位的,所以端口号也是16位的 2. UDP由于有效载荷的长度一开始就已经记录在UDP长度中了,每个UDP报文交付给上层其长度都是固定的,其报文和报文之间是有天然的边界的,不像TCP我们需要在应用层自己手动分离不同的TCP报文,UDP天然在内核中能分离,所以UDP叫做用户数据报 3. 由于我们的操作系统底层基本都是c语言写的,而UDP协议传递过程处于传输层,为操作系统内核,面对UDP协议的结构体传递时不需要序列化和反序列化,再把两主机结构体对齐和大小端问题统一,就可以直接传递结构体对象(这个结构体中都得是内置类型的),而用结构体对象指针取结构体的前8个字节就可以取到报头啦 ### UDP的特点 UDP 传输的过程类似于寄信 * **无连接**: 知道对端的 IP 和端口号就直接进行传输, 不需要建立连接 * **不可靠**: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方UDP协议层也不会给应用层返回任何错误信息 * **面向数据报**: 不能够灵活的控制读写数据的次数和数量 关于面向数据报: 应用层交给 UDP 多长的报文,UDP 原样发送,既不会拆分,也不会合并 用 UDP 传输 100 个字节的数据: > 如果发送端调用一次 sendto,发送 100 个字节,那么接收端也必须调用对应的一次recvfrom,接收 100 个字节; 而不能循环调用 10 次 recvfrom,每次接收10个字节 ### UDP的缓冲区 > • UDP 没有真正意义上的 发送缓冲区;调用 sendto 会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作 > > • UDP 具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报文的顺序和发送UDP报文的顺序一致(无法控制内核一定按顺序传递);如果缓冲区满了,再到达的 UDP 数据就会被丢弃 * UDP不需要发送缓冲区,TCP有发送缓冲区是因为它得确保如果数据在发送过程中弄丢了可以通过这个发送缓冲区中保留的数据进行重传,以此来确保数据德可靠性;但UDP不保证数据的可靠性,所以UDP不需要发送缓冲区 * UDP具有接收缓冲区是因为上层读取到报文之后需要时间进行处理,此时需要操作系统应具备收取报文得能力,所以才需要接收缓冲区 UDP 的 socket 既能读, 也能写, 这个概念叫做 **全双工** **UDP 使用注意事项** 我们注意到:UDP 协议报头中有一个 16 位的最大长度,也就是说一个 UDP 能传输的数据最大长度是 64K(包含 UDP 报头); (然而 64K 在当今的互联网环境下, 是一个非常小的数字------如果我们需要传输的数据超过 64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装) ### 基于 UDP 的应用层协议 • NFS: 网络文件系统 • TFTP: 简单文件传输协议 • DHCP: 动态主机配置协议 • BOOTP: 启动协议(用于无盘设备启动) • DNS: 域名解析协议 当然,也包括你自己写 UDP 程序时自定义的应用层协议 ## 3. 对报文的理解 先来看一个问题:**如果应用层正在进行报文的解析、处理,会不会影响os从网络中读取报文?为什么?** 答:**不会影响**,因为根据我们系统部分操作系统的运行原理方面的内容,应用层正在进行报文的解析、处理触发的是时钟中断,os从网络中读取报文触发的是外部的硬件中断,两者可能中断处理顺序有所不同,但是绝不会冲突,所以操作系统在处理从网络中读取报文的动作是不会收到应用层正在进行报文的解析、处理的动作的影响的,甚至可能是并行的! 所以通过上面的问题和解答,想让大家知道的是因为处理报文和读取报文之间不冲突,在处理一份报文的同时在我们的操作系统内部是很可能一下子收到多份报文的: ![image-20250904210811971](https://i-blog.csdnimg.cn/img_convert/31db4588a8cb7ec1c35ba8c4b599c5b8.png) 通过**先描述再组织**进行管理,我们用结构体sk_buff来描述这些报文 这种描述再用数据结构(如:链表)进行管理的操作和我们之间学习的进程pcb、文件struct_file没啥区别 ![image-20250904211754807](https://i-blog.csdnimg.cn/img_convert/830cb19a582b5d0331225e08642610e7.png) 这里的data指针初始指向是应用层数据的上边界,当报文从上往下送到传输层时报文前添加传输层协议报头,在图中data就指向的是TCP协议头的上边界,然后data和tail之间的完整报文再往下送到网络层时在报文前添加IP协议报头,对应到图中就是data再网上指向IP协议报头上边界,以此类推再送到下面时data再往上指到二层帧头的上边界了------这就是封装的过程,那么解包就反过来就行啦 (所以所谓的封装和解包,本质就是移动data指针在缓冲区更改指向,往往+/-的是对应层的协议长度) 封装的操作就是: ![image-20250905095453090](https://i-blog.csdnimg.cn/img_convert/e3e114e5b50de255ecea011016bed99f.png) 应用层数据发送到缓冲区,形成一个sk_buff结构体来进行描述;假设缓冲区前是低地址处,后是高地址处,先让sk_buff结构体中data和tail分别指向数据的边界,那么下层的sk_buff结构体变量要封装UDP协议报头就先让data减去报头占的字节(8个字节)然后data指针就往低地址处移动了,留出一块空间,然后再通过强转data指针到struct udphdr\*类型,给报头相应成员赋值就可以完成报头的插入封装工作了,之后是sk_buff结构体变量再往下传,下层做报头封装的方式也是一样的 做解包时一层一层向上传依次去掉报头,最后将有效载荷传到接收缓冲区中 ## 尾声 ok,相信大家看完上面的内容能对传输层协议有更进一步的了解,UDP是比较简单的,毕竟是不可靠协议就不需要关注那么多事情了,以效率为主,那么我们接下来要系统学习到的TCP协议为了保证其可靠性就多了很多特性内容啦

相关推荐
yukai080082 小时前
【203篇系列】041 Websocket Server
网络·websocket·网络协议
AC赳赳老秦2 小时前
轻量化模型浪潮下的关键技术突破:DeepSeek INT4量化优化引领2026端侧算力新纪元
网络·安全·mongodb·web安全·flink·prometheus·deepseek
御坂10101号2 小时前
JIT 上的 JIT:Elysia JS 的优化实践与争议
开发语言·javascript·网络·性能优化·node.js·express
DuHz2 小时前
汽车雷达高级信号处理和建模技术简介——文章精读(上)
linux·论文阅读·人工智能·汽车·信号处理
e***13622 小时前
linux 设置tomcat开机启动
linux·运维·tomcat
coollove74823 小时前
Linux下PostgreSQL-12.0安装部署详细步骤
linux·运维·postgresql
yuyuyuliang003 小时前
Ubuntu 22.04安装PostgreSQL教程
linux·运维·ubuntu·postgresql
清水白石0083 小时前
函数签名内省实战:打造通用参数验证装饰器的完整指南
java·linux·数据库
bepeater12343 小时前
Linux安装Redis以及Redis三种启动方式
linux·redis·bootstrap