前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >初识Linux · 传输层协议UDP

初识Linux · 传输层协议UDP

作者头像
_lazy
发布于 2025-05-08 03:27:17
发布于 2025-05-08 03:27:17
1290
举报
文章被收录于专栏:Initial programmingInitial programming

前言:

前文我们介绍了UDP的代码使用,流程也是非常简单的,创建了socket,然后填充对应的信息,最后bind,双方就可以进行通信了。通过代码的编写我们发现UDP是简单的,那么我们进入到源码方面学习UDP也是较为简单的。

今天的主题就是UDP协议的相关认识,比如涉及到了源码的指针,相关报文的管理,我们都会介绍到,话不多说,进入主题吧!

重新认识端口号和进程的关系

首先我们清楚的知道,最开始引入端口号的时候,是为了在网络世界中建议通信,那么IP地址+端口号就标识了网络世界中的唯一一个进程。

TCP/IP协议中,我们可以利用源IP 目的IP 源端口号 目的端口号 协议号这五元组来标识一个通信,在Xshell中我们可以使用netstat -n进行查看

然后我们在最开始介绍Socket编程的时候,我们知道了端口号是不能随便绑定的,因为端口号和服务器是强关联的,比如https服务器的默认端口号是443,http服务器的默认端口号是80,我们也可以在/etc/services中查看不同的服务器的默认端口号是多少:

所以我们有一个观点是:在编写网络通信程序的时候,要尽量避开这些知名端口号

那么在Linux内核中,进程和port的映射是通过一张哈希表来完成的,问题来了: 1.端口号是否可以被多个进程bind? 2.一个进程是否可以bind多个端口号?

对于以上两个问题,我们要清楚的是,端口号和进程之间是要保证端口号到进程的唯一性的,既然是唯一性,那么如果一个端口号被多个进程bind了,报文不管是向上交付的时候还是向下交付的时候,都会面临一个问题是:给哪个进程?

这里就和唯一性冲突了,那么反过来,一个进程如果bind了多个端口号,那么它的唯一性是确定的,因为我们不管是通过哪个端口号,都是只找到了一个进程。

以上是我们对端口号和进程关系的一个简单思考。


UDP协议

在我们理解上面这张图片之前, 我们回想一个知识点,在最开始引入网络编程的时候,我们花了一番功夫认识了协议是什么,我们仅仅是基于Linux网络编程的特点,认为协议就是通信双方都认识的结构体,那么在今天,我们将正式认识这个协议。

我们今天以及后续介绍协议都是使用的版本号为2.62.32.10。

UDP协议之所以简单,我们可以通过上述图片非常直观的感受出来,仅仅是通过5元组,就确定了一个报文。

它的成员变量只有四个,分别是两个端口号 数据的长度以及校验和,那么端口号我们理解,并且我们在编写代码的时候也是使用的uint16_t来接受的端口号,校验和我们这里不管。16位的UDP长度也是非常好理解的,它就是用来标识数据后面的长度嘛,以便OS进行封装和分用。

这里2.62.32.10中UDP的结构体设计,只能说非常简单了,source和dest就标识了端口号,len标识了长度。

那么问题来了,对于UDP来说,既然它的长度标识符的是16位的,那么意味着它的最大长度也只有64K左右,如果我们发送的数据超过了64K,就会导致一次发送数据发送不完,就要导致多出来的数据无效,甚至直接在系统层面报错。这个时候就需要我们手动的拆包分包了。

那么上述是UDP源码的简单理解。

对于UDP协议来说,它的特点是无连接,不可靠传输,面向数据报

对于无连接,我们拿TCP协议举例,TCP协议是要求进行connect,客户端和服务器进行连接,没问题再进行下一步操作,对于UDP协议来说,它只需要创建好套接字,然后填充对应的服务器信息,最后bind,bind完管你连接不连接呢,直接传输了就开始。

对于不可靠传输,因为UDP一旦传输失败,它什么也不管,也不会进行重传,这点和TCP有很大的区别,TCP有多种方案应付传输失败的情况,比如超时重传等。

对于面向数据报来说,它如果一次性发送100字节的数据,客户端就比如调用recvfrom接受100个数据,不能调用10次recvfrom接受10个字节的数据。而对于TCP来说,它100字节的数据可能分多次接受,就像打电话,别人说一句话,你没听清你可以让他再说一遍。

全双工

UDP协议也是支持全双工的,即它支持边读边写,不过它的发送缓冲区是没有TCP那种支持缓存管理机制的,比如超时重传,流量控制等机制的,所以它的缓冲区在应用层是无法感知的。这就导致了很多人认为它是没有缓冲区的,它调用逻辑是sendto之后立即交给内核,尽快发送。

报文的管理

UDP本身是简单的,那么我们返回到OS内核,我们不得不承认在操作系统中存在了很多的报文,这些报文有的在向上交付,有的在向下交付,那么如何管理报文本身就是一个问题,对于如何管理,我们向来都认为的是6个字:先描述再组织

那么今天我们就简单看一下报文的源码:

我们先不管sk_buff的两个指针,在操作系统看来,管理报文如果是杂乱无章的那可就太麻烦了,所以应该使用一种数据结构进行管理,在OS中使用的是双向链表的方式。

对于报文的基本格式是:[IP Header][UDP Header][Payload(你的数据)]

而报文都是在缓冲区的,我们在这里就需要考虑报文是如何向上解包和向下封装的了,这里利用到了sk_buff的两个指针,我们只要能找到UDP在缓冲区的那一部分,我们就可以操纵UDP里面的四个字段了。

所以这里用到了两个指针,分别是head和data:

一个结构体中的两个指针就分别指向了数据和UDP,那么数据因为是我们自己加的,我们就很清楚数据的地址,那么都在报文里面,我们可以head指针可以通过sizeof(udphdr)找到对应的udp的起始地址。

然后就可以对udp里面的字段进行赋值了,并且,这是一个报文的基本操作,对于多个报文,我们就可以使用next和prev进行多个报文的管理。

以上是对报文操作的一个简单理解。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档