【网络】对于我前面UDP博客的补充

news2024/12/24 20:22:02

UDP

  • 前言
  • 正式开始
    • UDP报文
      • UDP报文
        • 如何将UDP报文和报头进行分离和封装
        • UDP如何将有效载荷交付给上层
        • 如何提取出完整报文
        • 报头是啥
        • 报头中的检验和
      • UDP的特点
      • IO接口
      • 乱序问题
      • UDP是全双工的
      • 注意事项
      • 基于UDP的应用层协议
    • 再次谈论端口
      • 五元组
      • 端口号范围划分
      • netstat
      • xargs

在这里插入图片描述

前言

本篇比较偏理论。基于我前面一篇UDP实践的博客进行补充:【网络】网络编程入门篇——了解接口,快速上手,带你手搓简易UDP服务器和客户端(简易远端shell、简易群聊功能以及跨平台群聊)

主要讲解内容有:

  1. UDP协议报头讲解
  2. UDP协议缓冲区、注意事项等
  3. 五元组
  4. netstat命令讲解

正式开始

UDP报文

下面根据以下内容进行讲解:

  1. 任何协议都要解决两个问题:
    a. 如何分离/封装报头和报文
    b. 如何交付有效载荷
  2. 理解UDP报文本身
  3. 详谈具体报文字段

UDP报文

很简单,就这张图:
在这里插入图片描述

加点东西:
在这里插入图片描述

上面的8字节就是报头,存储了四个字段,每个字段两个字节。

很多教材上都有这张图,但是有的书上详细讲了,有的书没有讲,这里我来讲讲。

如何将UDP报文和报头进行分离和封装

采用定长报头的策略,UDP的报头为8字节的固定长度报头,对端传输层只需要将前8个字节提取出来,剩下的就是有效载荷。(等会说怎么提有效载荷)
在这里插入图片描述

无论是我前面博客中HTTP中用空行来区分报头和有效载荷,还是这里的用定长报头来区分报头和有效载荷,都是协议,双方都要严格遵守,那一层没有遵守哪一层就是一个无用的报文,无法向上交付。

UDP如何将有效载荷交付给上层

提取出报头后,其中有一个字段为目的端口号:
在这里插入图片描述

就通过这个目的端口号进行向上交付,因为进程bind了端口号,我前面写服务端的时候每次端口类型都是uint16_t,即2字节,因为协议中端口号是16位的。

如何提取出完整报文

先读固定长度的报头,报头中有16位的UDP长度字段:
在这里插入图片描述

这个16位的udp长度就是整个报文的长度,假如说是x,整个报文长度为x,报头长度位8,那么有效载荷的长度就是x - 8。读完报头后,再往后读x - 8个字节就行。

所以UDP具有将报文一个一个正确接收的能力。

报头是啥

报头其实就是一个结构体:

struct udp_hdr
{
	uint32_t src_port:16;
	uint32_t dst_port:16;
	uint32_t udp_len:16;
	uint32_t udp_check:16;
}

用结构体实现位段,如果你不懂位段的话可以看看我这篇博客:【C进阶】自定义类型。

再看一下这张图:
在这里插入图片描述

这里为什么要四字节为一行,因为结构体中用的类型是uint32_t,也就是4个字节,这里4个字节就是报头的宽度,所有的报头其实就是一个结构体类型。

封装一个报头,当应用层发来一个报文(就是传输层的有效载荷)时,传输层只需要定义一个udp_hdr对象,将对象中的字段填好,源端口、目的端口、报文长度(应用层的报文大小 + 8)、检验和,然后将这个对象中的数据拷贝到内核的缓冲区中就行。看图:
在这里插入图片描述

向上交付的时候就定义指针就行,报文长度和报头长度都有了,就根据这两个长度读取就行。

这里只是简单的原理,os真正做起来的话要比这复杂的多,毕竟要考虑大小端、平台位数等问题,还是很复杂的。

报头中的检验和

这个检验和不细讲了,说一下有啥用。

接收方UDP检验成功就向上交付,检验失败报文就直接丢弃了,发送方不知道、不重传、不关心。

UDP的特点

其实我前面那篇UDP的博客中也讲过了,这里就再说一下。

  • 无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;

如果你看了我前面手搓UDP服务器和TCP服务器的话,你一定懂这句话是什么意思。

因为UDP的服务器只需要这几步:

  1. 创建套接字
  2. bind绑定端口号
  3. recvfrom接收客户端请求并用sendto进行响应

而TCP的服务器是这样的:

  1. 创建套接字
  2. bind绑定端口号
  3. 设置套接字为listen状态
  4. 接收连接
  5. recv/read获取客户端请求并通过write/send进行响应

这里UDP要比TCP少了listen和接收连接这两步,也就是UDP不需要建立连接。

  • 不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息;

下面我截一下前面我UDP博客中的一段话:

屏幕前的你先不要拿这一点来判断谁好谁坏。TCP和UDP都能够存在说明是都能被接受的,各有各的特色。
  先说点UDP不好的,UDP会出现丢包问题,TCP不会出现。不过现在的网络出现丢包问题概率并不大,即使出现了大部分场景下也是可以容忍的。先不要对UDP产生偏见,等会讲的时候你就知道为啥了。
  二者的可不可靠只是二者的特点。并不是单方的缺点。
TCP比UDP可靠,那么TCP就要做更多的工作,可靠性是要靠大量的编码和数据处理工作来实现的,所以TCP想要保证可靠性就一定会使得其在数据通信时为了可靠性而设计出更多的策略,如面向连接、确认应答、超时重传、流量控制、拥塞控制等机制,而这些机制都要靠TCP协议自己来完成,所以说虽然保证了可靠性,但是也就导致了该协议更复杂,维护起来成本更高。

此时UDP就可以对TCP说:咋俩都是协议,你把自己搞那么累干嘛,丢包就丢包了,心放大点,这都不是事,所以UDP只要把数据交给下层就行了,然后就啥也不用管了。而TCP就得管一大堆事,即TCP更安全,UDP更简单。

但是我们大部分情况下传输层协议用的是TCP。像直播、视频这种数据可以用UDP,比如我们有时候看着直播 / 视频时突然卡一下/没声了/屏幕花了等等问题,就可能是因为传输层用的是UDP协议导致丢包了,但是这样就卡一两秒对整体的观看体验影响不大(足球比赛快要进球的时候卡了当我没说😅)。不过有钱的公司也在直播或视频这种资源上也可以用TCP协议,不过TCP协议花费更多,但是能够保证数据的安全,客户能有更好的体验。

  • 面向数据报: 不能够灵活的控制读写数据的次数和数量

IO接口

前面TCP和UDP中IO的接口本质上都是拷贝函数。

TCP的 write、send和read、recv。
UDP的 sendto和recvfrom。

都是拷贝函数。

在这里插入图片描述

所以这里的wirte、send、sendto都不是直接将数据发送到网络当中的,而是将数据拷贝到了内核缓冲区中就完事了,后续工作都是os做的。

TCP和UDP是传输层中应用最广泛的协议,而发送缓冲区(UDP不需要,等会说)和接收缓冲区是由传输层提供的,传输层决定什么时候发,发多少,出错了怎么办的问题,我们无法决定这些问题,就像寄快递一样,你只是把东西交给了快递公司,至于快递啥时候发是快递公司的事情,不是我们操心的。

UDP不需要发送缓冲区,调用sendto只是将数据拷贝给os,os会立即将数据经过网络层、数据链路层发送出去,所以对于发送缓冲区没有特别强的需求。

但是UDP需要接收缓冲区,UDP上面是应用层,应用层就是用户,也就是程序员,需要手动调用recvfrom,但是如果程序员来不及调用呢?同一时刻服务端可能会接收到很多的UDP报文请求,如果某些报文来不及接收,就会先放到接收缓冲区中,所以UDP需要接收缓冲区,但如果缓冲区满了,那么后面到来的报文会被直接丢弃。

乱序问题

如果某一时刻客户端发送了5个报文,如果是按照ABCDE的顺序来发的话,可能会出现接收到的报文为BDCEA的顺序,就像我们在某宝上下单一样,同一天下的单,后面收到的时间不一样,顺序也不一样。

UDP乱序问题通常是因为路由之间的存储转发引起的。解决方法是在发送端的数据段中加入数据报序号的方式,只需要在接收方对数据的头端进行简单排序处理即可。具体步骤如下:

  1. 在发送端的数据段中加入数据报序号。
  2. 接收方接收到数据后,将数据报文按照序号进行排序。
  3. 将排序后的数据报文进行组装,得到完整的数据。

需要注意的是,如果数据报文丢失,可能会导致数据报文序号不连续,因此需要在接收方设置一个超时时间,如果在超时时间内没有收到连续的数据报文,则需要重新请求发送方发送数据报文。

不过这里的做法已经很像TCP了,下一篇博客我会详细讲解TCP。

UDP是全双工的

一个文件描述符,再多线程场景下既可以读也可以写,如何保证呢?
只需要读写缓冲区不冲突就行。

UDP虽然没有发送缓冲区,但是发送时并不会影响接收缓冲区,所以支持全双工。

至于全双工和半双工是啥我实在是不想讲了,前面博客中说了好几次了,不懂的同学搜一下或者翻一下我前面的博客吧。

后一篇要讲的TCP也是全双工的。

注意事项

我们注意到, UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部)。然而64K在当今的互联网环境下, 是一个非常小的数字。如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装。

基于UDP的应用层协议

  • NFS: 网络文件系统
  • TFTP: 简单文件传输协议
  • DHCP: 动态主机配置协议
  • BOOTP: 启动协议(用于无盘设备启动)
  • DNS: 域名解析协议

当然, 也包括你自己写UDP程序时自定义的应用层协议;

说说DHCP,当你的电脑没连网的时候,没法上网,本质上是你的电脑没有IP,当连接上WiFi的时候(或者其他能连网方法),电脑会自动获取一个IP地址,这个IP是路由器给的,路由器内部署了DHCP服务,关了电脑之后IP会被自动回收。

再次谈论端口

再我前面写UDP和TCP服务器时,都要让服务端bind一个端口号,客户端也要绑定,但是不需要我们手动绑定,os会自动帮我们做。当本机拿到数据后向上交付时,要根据端口号交付给特定进程。

在这里插入图片描述

系统是如何快速找到端口号对应的进程的呢?
用哈希,可以理解为K值就是端口号,V值就是进程ID,要把有效载荷提供给应用层,就要找到进程的文件描述符,通过文件描述符把有效载荷拷贝到通信的文件中即可,找到文件描述符就先找到进程PCB就行,就这样通过PID找PCB,然后通过PCB找文件描述符。

五元组

在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信。

源IP和源端口号标定发送方的唯一进程。
目的IP和目的端口号标定接收方唯一进程。
协议号可以理解为标定传输层用的是哪一个协议,通常就是TCP或UDP。

这样的一个五元组就可以标定一个通信:
在这里插入图片描述

端口号范围划分

端口号是一个16位的二进制数,所以取值范围为0~65535。

0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的.
就像是110就是警察,120就是急救,119就是火警一样。

有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号:

  • ssh服务器, 使用22端口
    ftp服务器, 使用21端口
    telnet服务器, 使用23端口
    http服务器, 使用80端口
    https服务器, 使用443端口

/etc/services里面存放了常见端口号。
在这里插入图片描述

不要随意绑定0 ~ 1023的端口号。

1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的。我们自己也能选择其中的进行绑定。像我前面我在搞服务器的时候端口号用的基本上都是8080。

netstat

netstat是一个用来查看网络状态的重要工具.
语法:netstat [选项]
功能:查看网络状态
常用选项:

  • n 拒绝显示别名,能显示数字的全部转化成数字
  • l 仅列出有在 Listen (监听) 的服務状态
  • p 显示建立相关链接的程序名
  • t (tcp)仅显示tcp相关选项
  • u (udp)仅显示udp相关选项
  • a (all)显示所有选项,默认不显示LISTEN相关

在这里插入图片描述
这里查看的时候带的选项是natp。

n就是能显示成数字的就显示成数字,看第二行的sshd,如果我这里去掉n:
在这里插入图片描述
就变成了ssh,上面将协议固定端口的时候也说了,ssh的端口就是22。

a就是把状态为listen的也显示,我去掉a之后:
在这里插入图片描述

t就是显示tcp的服务,t换成u就是显示udp的服务:
在这里插入图片描述

p就是显示进程和其PID,也就是最后面的字段,去掉:
在这里插入图片描述

这里udp在接收服务的时候不会进行listen,至于什么原因我前面的博客中有,代码写过就知道了。

再看回来前面的这张图:
在这里插入图片描述

来看看sshd进程:
在这里插入图片描述

这个进程是一个守护进程(前面博客讲过,不懂点链接:【网络】用代码讲解协议 + 序列化和反序列化 + 守护进程 + jsoncpp),大部分以d结尾的进程一般都是守护进程(d就是daemon),三个ID相同,父进程是1os。

我这里用的是云服务器,登录的时候sshd会在终端为我开一个bash进程,并让我输入指令,输入后就将字符串发送给远在异地(北京、广东等)的服务器执行,并将执行后的结果返回。

xargs

这是一个命令行上的东西,可以将标准输入的内容转为命令行参数。

比如说我用pidof来获取一个进程的pid,然后用管道 + args交给kill命令,就可以关掉这个进程。
在这里插入图片描述

kill命令想使用的话必须得是命令行参数来搞,管道传输的数据是通过文件(标准输入)来实现的,没法直接给成命令行参数:
在这里插入图片描述
这里管道会将pidof的结果转成标准输入送给kill -9,但是kill -9执行的时候是将各个字段通过命令行参数来交给进程的,标准输入和命令行参数没有啥关系,无法直接执行kill命令,但是xargs会将标准输入的转成命令行参数,这样就可以传给进程执行了。

再比如说ls | xargs touch可以更新所有文件的ACM时间,改成最新的时间:
在这里插入图片描述

UDP比较简单,没有什么好讲的,你只要能写出我前面简易UDP服务器,懂本篇所讲的UDP报头就行。

到此结束。。。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#调用C++ 的DLL传送和接收中文字符串

1 c#向c传送中文字符串 设置&#xff1a;将 字符集 改为 使用多字节字符集 cpp代码&#xff1a; extern "C"_declspec(dllexport) int input_chn_str(char in_str[]) {cout<<in_str<<endl;return 0; }c#代码&#xff1a; [DllImport("Demo.dll…

uni-app:引用文件的方法

绝对定位 ①import common from "/utils/common.js" ②import common from "utils/common.js" <template><view></view> </template> <script>import common from "/utils/common.js"export default {data() {ret…

分享一下抽奖活动小程序怎么做

在当今数字化时代&#xff0c;抽奖活动小程序已成为一种高效、创新的营销方式。它不仅能够吸引用户的注意力&#xff0c;提高品牌知名度&#xff0c;还能促进用户参与度&#xff0c;增强用户对品牌的忠诚度。本文将详细介绍如何制作一个成功的抽奖活动小程序&#xff0c;以及它…

[yolo系列:如何固定随机种子(以yolov7为例)]

文章目录 概要随机种子&#xff08;Random Seed&#xff09;第一步第二步第三步第四步 概要 在计算机科学和深度学习领域&#xff0c;随机性是一个常见而重要的概念。在一些情况下&#xff0c;我们需要确保代码每次运行时都能得到相同的随机结果&#xff0c;以便进行模型的可重…

SAR ADC:10 bit 串口控制的模数转换器MS1549,可替TLV1549

MS1549 是一个 10 位开关电容器、逐次逼近型的模数转换 器。此芯片有 2 个数字输入端、 1 个三态输出口&#xff08;包括片选端 口 ( CS ) 、 1 个 I/O CLOCK 端口和 1 个数字输出端 (DATA OUT) &#xff09;&#xff0c; 可以实现三总线接口到总控制器的串行口的数据传输…

正点原子嵌入式linux驱动开发——异步通知

上一篇笔记中使用阻塞或者非阻塞的方式来读取驱动中按键值都是应用程序主动读取的&#xff0c;对于非阻塞方式来说还需要应用程序通过poll函数不断的轮询。最好的方式就是驱动程序能主动向应用程序发出通知&#xff0c;报告自己可以访问&#xff0c;然后应用程序再从驱动程序中…

Redis持久化解析:全面了解Redis的数据持久化机制

文章目录 &#x1f34a; Redis持久化&#x1f389; 什么是持久化&#xff1f;&#x1f389; 持久化的原理&#x1f4dd; RDB持久化&#x1f4dd; AOF持久化&#x1f4dd; 混合持久化&#x1f4dd; save与bgsave &#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、Java领…

使用 VS 2022 开发C#项目的tips

代码操作 删除注释或空行 参考C#【必备技能篇】Visual Studio删除所有的注释和空行 删除所有行注释&#xff1a;包括行内和行外&#xff0c;如下所示 Ctrl H 替换&#xff0c;第一行输入&#xff1a;//[^\n]*\n, 第二行输入&#xff1a;\n。替换即可。 这一步可能出现很多空…

数据结构题型20-第七章 查找

文章目录 1 考察重点2 知识框架3 考察重点4 顺序查找和折半查找4.1 顺序查找4.1.1 一般线性表的顺序查找4.1.2 有序表的顺序查找 4.2 折半查找4.3 分块查找 1 考察重点 2 知识框架 3 考察重点 4 顺序查找和折半查找 4.1 顺序查找 4.1.1 一般线性表的顺序查找 4.1.2 有序表的顺…

基于Django开发的图书管理推荐、电影推荐、课程推荐系统、大众点评店铺推荐系统、健身课程管理系统

基于Django开发的图书管理推荐、电影推荐、课程推荐系统、大众点评店铺推荐系统、健身课程管理系统、资讯推荐系统 一、简介 推荐系统的目的是信息过载所采用的措施&#xff0c;面对海量的数据&#xff0c;从中快速推荐出用户可能喜欢的物品。 推荐系统的方法有以下几种&…

顺序栈的实现----数据结构

栈的概念 对于栈&#xff08;Stack&#xff09;&#xff0c;后进先出&#xff08;Last In First Out&#xff0c;LIFO&#xff09;&#xff0c;栈也是一种线性表&#xff0c;只不过是一种操作受限的线性表&#xff0c;只能在一端操作&#xff0c;也就是不允许在中间进行查找、…

javaEE -4(11000字详解多线程)

一&#xff1a;常见的锁策略 1.1 乐观锁 vs 悲观锁 乐观锁和悲观锁是并发控制的两种不同策略&#xff0c;用于处理多个线程同时访问共享资源的情况。它们的主要区别在于对并发冲突的处理方式。 悲观锁是一种较保守的并发控制策略&#xff0c;它假设在整个事务过程中会发生冲…

什么是云迁移?迁移到云的基本指南

企业可以执行多种类型的云迁移。一种常见的模型是将数据和应用从本地数据中心传输到公共云。然而&#xff0c;云迁移还可能需要将数据和应用从一个云平台或提供商迁移到另一个云平台或提供商;这种模式称为云到云(C2C)迁移。第三种类型的迁移是反向云迁移、云遣返或云退出&#…

如何提高webpack的构建速度?

一、背景 随着我们的项目涉及到页面越来越多&#xff0c;功能和业务代码也会随着越多&#xff0c;相应的 webpack 的构建时间也会越来越久 构建时间与我们日常开发效率密切相关&#xff0c;当我们本地开发启动 devServer 或者 build 的时候&#xff0c;如果时间过长&#xff…

Linux下的IMX6ULL——环境搭建与软件安装(一)

前言&#xff1a; 从今天开始我们就要用到IMX6ULL这块开发板进行linux的学习了&#xff0c;对于初学者&#xff0c;下载好资料、搭建好开发环境后&#xff0c;按照下面顺序学习即可&#xff1a; 先应用&#xff0c;再驱动&#xff0c;最后做项目。应用、驱动、项目这三个慢慢走…

酷开科技丨大屏购物买买买,酷开系统助力网购模式再升级

随着技术的发展和家庭场景智能化的趋势&#xff0c;消费者对品质和体验的需求不断提高。在这一背景下&#xff0c;OTT大屏营销生态得到了快速发展&#xff0c;大屏的购物功能进一步被释放&#xff0c;已经具备更强的“转化”能力。电视的智能化、交互简单化、账号同步化等特性使…

如何利用验证链技术减少大型语言模型中的幻觉

一、前言 随着大型语言模型在自然语言处理领域取得了惊人的进步。相信深度使用过大模型产品的朋友都会发现一个问题&#xff0c;就是有时候在上下文内容比较多&#xff0c;对话比较长&#xff0c;或者是模型本身知识不了解的情况下与GPT模型对话&#xff0c;模型反馈出来的结果…

Linux基础指令(二)

目录 前言 1. which 2. alias 3. cat 3.1 echo指令 3.2 输出重定向 3.3 cat指令 3.4 * cat其他用法 4. more指令 5. less指令 6.head指令 7. tail 指令 8. 时间相关的指令 9. cal指令 总结 前言 Linux环境下的指令本质是一组可执行的命令&#xff0c;它们被编写成可…

商家在哪里可以制作小程序抽奖活动

在当今数字化时代&#xff0c;小程序已经成为一种普及且高效的营销工具。对于商家而言&#xff0c;利用小程序开展抽奖活动是一种极具吸引力的营销策略。本文将详细介绍商家如何创建小程序抽奖活动&#xff0c;并阐述其带来的营销效果。 一、明确活动目的与方案 在开始制作小程…

图纸管理制度《一》

在不断进步的时代&#xff0c;我们都跟制度有着直接或间接的联系&#xff0c;制度一般指要求大家共同遵守的办事规程或行动准则&#xff0c;也指在一定历史条件下形成的法令、礼俗等规范或一定的规格。那么什么样的制度才是有效的呢? 彩虹图纸管理软件_图纸管理系统_图纸文档管…