传输层协议之UDP

1、端口号

        我们在应用层创建的套接字,是需要通过bind()接口绑定我们的IP地址与端口号的,这是因为数据从传输层向上交付到应用层时,需要用端口号来查找特定的服务进程。一般在网络通信时,用IP地址标识一台主机,用端口号表示该主机上特定的服务。所以一台主机上,可能同时存在各种不同的服务,每一种服务都有它对应的端口号。在传输层,系统会根据我们的端口号找到应用层的进程,将我们的数据交给应用层。所以网络通信最后实际上是把数据包向上根据端口号交付给特定进程。在TCP/IP协议中,使用五元组(源IP、源端口、目的IP、目的端口、协议号)保证通信中进程的一对一。

        端口号的是16位无符号整数,所以取值范围应该是0~65535,实际上在使用云服务器时,我们在bind()时,会发现有时候bind不了,因为在云服务器中的一些端口号是知名端口号,我们熟知的HTTP、FTP、SSH这些广泛使用的应用层协议,它们有自己的端口号,一般是固定不变的,被业界广泛承认固定,所以我们无法绑定这些端口号。知名端口号范围是0~1023,从1024~65535才是操作系统动态分配的端口号,一般客户端在随机分配端口的时候,是从这个范围中分配的。

        有两个问题,(1)一个进程能否绑定多个端口号?(2)一个端口号能否被多个进程绑定?答案分别是可以和不可以。数据在交付时,通过网络分层,一定是自底向上进行交付的,所以一定要保证从端口号到进程的唯一关系。而一个进程绑定多个端口号其实不破坏端口号到进程的唯一关系,所以是可以的。举个例子,我们可以写一个服务器,他有一个80号端口使用TCP协议,我们为其创建一个套接字,我们在创建一个使用UDP协议的套接字,绑定81号端口,在网络通信时既可以使用80号端口也可以使用81号端口,它们都与一个进程绑定,可以用不同的端口提供不同的服务,比如使用UDP端口发指令,使用TCP端口发数据。

UDP

1、报文格式        

        下面这张图就是UDP报文的格式分布,最下面有一个数据,代表应用层向下交付的所有数据,也成为UDP报文的有效载荷。所以我们在应用层使用套接字和sendto接口发送数据时,并不是直接讲数据发送到网络中去,而是发给了传输层,UDP协议会为我们的有效载荷进行添加报头封装,形成完整的UDP报文,进而进一步向下交付。

        具体来看,UDP报头里面包含16位源端口,16位目的端口,16位UDP长度和16位UDP校验和。在我们进行应用层代码编写时,写套接字时,我们绑定端口号为什么一定要使用uint_16?因为传输层和网络层时是属于操作系统内容,是由Linux内核进行管理的,在操作系统内部,端口号使用16位来表示的,这就决定了我们在应用层也要使用16位进行设置。16位校验和是UDP保证数据基本的正确性的一种策略。

        那么UDP报文在传输层是如何做封装和解包的?大部分的应用层协议为了让报头和有效载荷分离,要么规定特殊符号,比如\r\n来标识报头和有效载荷,要么直接在报头设置文件描述字段,比如在自己的报文前面带上长度。在UDP这里是怎么实现的呢?非常简单,UDP采用的策略叫做定长报头,传输层如果发现接收到的报文是一个UDP报文,内核中会直接把报文的前八个字节移走,移走之后再把有效载荷向上交付就可以了。这种策略可以说是所有协议中,设计最简单的,大小固定,在收发通信时,报头长度永远都是固定的,报头一旦固定约定好,客户端和服务器都认为报头是八个字节,所以再封装的时候加八个字节,解包的时候提取前八个字节,剩下的都是有效载荷。

        传输层上面还有很多的应用层协议(包括http、https、ssh),在传输层解包之后,后续的行为叫做对报文分用,什么是分用?是指传输层把报头和有效载荷分开之后,将有效载荷交给上层的特定协议。如何实现分用?报头中有16位目的端口号,系统根据目的端口号找到特定的进程,也就是应用层协议,将有效载荷向上交付,报文在向上交付到指定进程这个行为,其实就是交付到指定的应用层协议,可以认为应用层协议、进程、端口号是三位一体的

        我们可以看到报头中有一个字段叫做16位UDP长度,这个长度代表整个UDP报文的长度(包含报头),这也就意味着整个UDP报文的最大长度是2的16次方,也就是64kb,在当前的网络环境中,64kb其实是一个很小的数据量,如果我们要在传输层使用UDP协议传输大于64kb的数据,我们必须要在应用层将报文拆成64kb一下的数据,不然会发送失败。

        

2、UDP的特点

        (1)无连接。我们使用UDP协议实现一个服务器与客户端通信功能时,客户端建立套接字,bind自己的ip地址和端口号,并知名目的端口和ip,就可以直接发送消息,就像我们发送邮件时,是不需要确认自身与接收方的连接关系的。不像TCP在正式通信之前要经过三次握手确认两端已经连接好了才能发送数据。

        (2)不可靠。不论任何协议,在数据传输过程中可能发生丢包问题,一个报文在路上要经过无数个主句,无数个路由器,无数个转发设备进行转发,所以丢包其实是一个很常见的情况。关键在于不同的协议面对丢包的处理方式不同,UDP协议下,丢包了之后什么都不做,这就是所谓的不可靠。而TCP为了保证可靠性设计了一系列策略,超时重传、连接管理、流量控制、拥塞控制等。

        (3)面向数据报。首先,面向数据报我们可以类比为收发快递,当商家给我们发送三个快递,我们一定是要收三个快递的,不能只收一个或者一个半或者五个,不存在我们取快递时先取走半个,下次再取后半个,必须整发整取。在实际代码层面,服务器接收客户端报文,调用recvfrom接口时,要么别读,要么调用recvfrom成功时,必定读取到一个完整的报文,客户端发了十个报文,代表其调用了10次sendto接口,服务端也必须调用10次recvfrom接口,这个次数是一比一的(在不考虑丢包的前提下)。所以在写代码时,在UDP协议下,读取报文时不需要验证读取报文的完整性的,读取到报文之后只需要考虑数据的序列和反序列化(结构化)问题就可以了,而在TCP协议下,基于其面向字节流的特性,我们在应用层还需要设置一些验证报文完整性的策略。

        这里讲讲面向字节流,它的特点就是,发送端发数据可能发了十几二十次,但是接收方并不知道发送端发了多少次,上层也不知道报文与报文之间在传输层有什么样的边界,发了十多次,接收方可能一次就把数据读完了,也可能100次才读完。至于如何保证读取报文的完整性,程序员需要在应用层自己去定协议,自己从字节流中提取一个完整的报文。

3、UDP的缓冲区

        在讲UDP的缓冲区之前,先通过了解TCP的缓冲区对传输层缓冲区有一个系统的了解。

        这一段所讲的全部都是在TCP协议下的。在应用层我们调用的对套接字进行操作的接口,诸如read、write、send等等,我们在调用这些接口时并没有把数据从应用层,直接发送到网络里,我们只是通过这样的接口把数据交给了下层传输层,然后再继续向下交付。需要明确一点,我们用的这些网络IO接口,其实并不是直接发送,而是拷贝接口。在TCP这样的协议下,实际上通信双方会在各自的传输层维护发送和接收缓冲区,客户端和服务器都有,在调用send和write接口时,我们应用层中也要维护一块缓冲区,也许是一个char buffer[1024],我们从标准输入流中拿到数据也需要先保存到应用层的缓冲区中,接着调用send和write时,我们并没有直接将数据发送到网络中,而是把应用层的数据拷贝到自己的发送缓冲区中。拷贝好了之后,再由传输层,也就是操作系统,来控制发送缓冲区里的数据什么时候发?发多少?最终经过网络把数据放到对方的接收缓冲区里了。客户端读取时,也并不是从网络里读取上来,而是从接收缓冲区把数据拷贝到应用层中。TCP协议下,通信双方都维护了自己的发送缓冲区和接收缓冲区,可以同时实现两个方向的数据流动,互不干扰,这样的通信方式,称之为全双工。当应用层通过调用send或者write接口把数据交付给TCP之后,接口就直接返回了,相当于数据交付给了操作系统,后续什么时候发,发多少,丢包了怎么办,由操作系统来自主地决定(这也是为什么TCP叫做传输控制协议),应用层可以继续进行后续的业务,所以传输层缓冲区存在的价值,除了支撑全双工,还能够直接提高我们发送数据的效率。总而言之,用户把数据从应用层拷贝到对应的操作系统内部,操作系统再把数据从缓冲区刷新到网络里,有人放有人取,这个模型特别像之前提过的生产消费者模型,也是发送端和接收端进行解耦,解决忙闲不均问题。

        以上就是我们的传输层缓冲区的概念,可以联想到我们使用系统调用读写文件,我们基于文件描述符对文件进行读写,调用write时并不是把数据直接写到了磁盘上,因为IO太费时间了,调用write只是将数据拷贝到内核维护的一块缓冲区中,系统会等到数据到达一定数量或者读到\n再把数据刷新到磁盘上。

        现在来谈谈UDP,其实UDP没有真正意义上的发送缓冲区,它不需要发送缓冲区,应用层的数据交付到传输层,UDP直接添加上所谓的报头,直接交给在下一层,它不用支持可靠性机制,所以不需要暂时把数据暂存下来。我们应用层调用sendto把数据交给操作系统操作系统将数据传输给网络层,之后进行后续的传输动作。而UDP是具有接收缓冲区的,用于保存收到的数据,本质上,也是为了应对传输层收到数据了,而应用层还在对上一次接收到的数据进行处理而来不及接收的问题。这个接收缓冲区不保证数据有序,即不保证接收到的数据和发送的数据顺序一致,乱序本身是不可靠的一种情况。如果缓冲区中的数据满了,后续的数据会直接丢弃,这时UDP的处理策略。UDP整体上是支持通信双方同时读写的,因此它也具有全双工的特点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/375748.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

el-table 动态添加删除 -- 鼠标移入移出显隐删除图标

<el-table class"list-box" :data"replaceDataList" border><el-table-column label"原始值" prop"original" align"center" ><template slot-scope"scope"><div mouseenter"showClick…

Kubelet 认证

当我们执行kubectl exec -it pod [podName] sh命令时&#xff0c;apiserver会向kubelet发起API请求。也就是说&#xff0c;kubelet会提供HTTP服务&#xff0c;而为了安全&#xff0c;kubelet必须提供HTTPS服务&#xff0c;且还要提供一定的认证与授权机制&#xff0c;防止任何知…

Bertopic环境安装与文本主题聚类

文章目录 1.环境配置(一)安装:anaconda1. 理解:为什么需要anaconda2. 下载anaconda3. 启动anaconda(二)安装:python环境(三)安装:依赖包hdbscan的安装问题解决方案1. 安装build-tools-for-visual-studio2. 安装hdbscan(四)安装transformers、BERTopic等重要依赖包2…

FlinkModule加载HiveModule异常

HiveModule这个模块加载不出来 加在不出来这个模块&#xff0c;网上查说是要加下面这个依赖 <dependency><groupId>org.apache.flink</groupId><artifactId>flink-connector-hive_${scala.binary.version}</artifactId><version>${flink.…

GPIO通用输入输出口

可配置八种输入输出模式&#xff1b; 引脚电平&#xff1a;0~3.3V&#xff0c;部分引脚可容忍5V&#xff1b;&#xff08;可以输入5V&#xff0c;输出最大只能是3.3V&#xff09; 带FT的是可以容忍5V的不带FT的就只能接入3.3V的电压。 输出模式下可以控制端口输出高低电平&am…

【算法】代码随想录之链表(更新中)

文章目录 前言 一、移除链表元素&#xff08;LeetCode--203&#xff09; 前言 跟随代码随想录&#xff0c;学习链表相关的算法题目&#xff0c;记录学习过程中的tips。 一、移除链表元素&#xff08;LeetCode--203&#xff09; 【1】题目描述&#xff1a; 【2】解决思想&am…

自动驾驶中,实现三维点旋转原理

文章目录 1. 三维点旋转的方案2. 使用复数表示二维点的旋转2.1. 复数的概念2.2. 复数的三种形式及相互转换2.3. 复数概念扩展&#xff1a;实数、虚数、复数 3. 四元数旋转三维点原理4. 使用四元数进行旋转的公式5. 旋转叠加6. 四元数转换为三维点7. 代码实现 1. 三维点旋转的方…

13 协程设计原理与汇编实现

协程的问题 为什么要有协程?协程的原语操作?协程的切换?协程的struct如何定义?协程的scheduler(调度)如何定义?调度策略如何实现?协程如何与posix,api兼容?协程多核模式?协程的性能如何测试?为什么要有协程 同步的编程方式,异步的性能。同步编程时,我们需要等待io就…

14-47 剑和诗人21 - 2024年如何打造AI创业公司

​​​​​ 2024 年&#xff0c;随着人工智能继续快速发展并融入几乎所有行业&#xff0c;创建一家人工智能初创公司将带来巨大的机遇。然而&#xff0c;在吸引资金、招聘人才、开发专有技术以及将产品推向市场方面&#xff0c;人工智能初创公司也面临着相当大的挑战。 让我来…

ant design form动态增减表单项Form.List如何进行动态校验规则

项目需求&#xff1a; 在使用ant design form动态增减表单项Form.List时&#xff0c;Form.List中有多组表单项&#xff0c;一组中的最后一个表单项的校验规则是动态的&#xff0c;该组为最后一组时&#xff0c;最后一个表单项是非必填项&#xff0c;其他时候为必填项。假设动态…

SQL,python,knime将数据混合的文字数字拆出来,合并计算实战

将下面将数据混合的文字数字拆出来&#xff0c;合并计算 一、SQL解决&#xff1a; ---创建表插入数据 CREATE TABLE original_data (id INT AUTO_INCREMENT PRIMARY KEY,city VARCHAR(255),value DECIMAL(10, 2) );INSERT INTO original_data (city, value) VALUES (上海0.5…

对照ui图进行大屏幕适配,echerts适配

1.先找到ui图&#xff0c;我这边是1920*1080的屏幕进行的设计 2.在界面找到跟样式的字体大小&#xff0c;进行设置&#xff0c;一般ui设置字体大小便可 3.在js中写入原生js代码 function adapter() {//获取布局视口宽度&#xff0c;布局视口设备横向独立像素值const dpWidth…

Stable Diffusion 深度探索:从入门到精通的全方位教程

在人工智能艺术创作的浪潮中&#xff0c;Stable Diffusion 作为一股不可忽视的力量&#xff0c;正以其独特的魅力吸引着无数创作者和科技爱好者的目光。本文旨在为大家提供一份详尽的 Stable Diffusion 教程&#xff0c;从基础概念到高级应用&#xff0c;带领你一步步走进这个充…

paddla模型转gguf

在使用ollama配置本地模型时&#xff0c;只支持gguf格式的模型&#xff0c;所以我们首先需要把自己的模型转化为bin格式&#xff0c;本文为paddle&#xff0c;onnx&#xff0c;pytorch格式的模型提供说明&#xff0c;safetensors格式比较简单请参考官方文档&#xff0c;或其它教…

【eNSP模拟实验】单臂路由实现VLAN间通信

实验需求 如下图所示&#xff0c;辅导员办公室需要访问处在不同vlan的学生管理服务器的文件&#xff0c;那么如何实现两台终端相互通信呢&#xff1f;我们可以使用单臂路由的方式来实现。 单臂路由&#xff08;router-on-a-stick&#xff09;是指在路由器的一个接口上通过配置…

数据结构:链表详解 (c++实现)

前言 对于数据结构的线性表&#xff0c;其元素在逻辑结构上都是序列关系&#xff0c;即数据元素之间有前驱和后继关系。 但在物理结构上有两种存储方式&#xff1a; 顺序存储结构&#xff1a; 使用此结构的线性表也叫 顺序表物理存储上是连续的&#xff0c;因此可以随机访问…

CAS详解

文章目录 CAS使用示例Unsafe类实现原理CAS问题 CAS CAS全称为Compare and Swap被译为比较并交换&#xff0c;是一种无锁算法。用于实现并发编程中的原子操作。CAS操作检查某个变量是否与预期的值相同&#xff0c;如果相同则将其更新为新值。CAS操作是原子的&#xff0c;这意味…

美团收银Android一面凉经(2024)

美团收银Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《美团收银Android一面凉经(2024)》。 应聘岗位: 美团餐饮PaaS平台Android开发工程师…

使用offset explorer 3.0连接单机版kafka

一、目标 使用kafka图形化工具offset explorer 3.0连接单机版的kafka 二、windows下载安装offset explorer 3.0 1、kafka tool工具官方下载页面 Offset Explorer https://www.kafkatool.com/download.html 2、安装offset explorer 3.0 下一步&#xff0c;下一步&#xff0…

【微信小程序知识点】手机号验证组件

手机验证组件&#xff0c;用于帮助开发者向用户发起手机号申请&#xff0c;必须经过用户同意后&#xff0c;才能获得由平台验证后的手机号&#xff0c;进而为用户提供相应的服务。 手机号验证组件分为两种&#xff1a;手机号快速验证组件以及手机号实时验证组件。 1.手机号快速…