【Linux网络编程】传输层协议-----UDP协议
文章目录
- 一、前置知识了解:
- 1、传输层意义:
- 2、端口号:
- 3、五元组标识一个通信:
- 4、netstat
- 5、pidof:
- 二、UDP协议:
- 1、UDP协议格式:
- 报头解析:
- 源端口和目的端口:
- UDP长度和UDP检验和:
- 如何理解报头:
- 2、面向数据报
- 3、UDP缓冲区:
一、前置知识了解:
1、传输层意义:
应用层的协议并不是简单地将请求和响应直接发送到网络中的,而是将数据通过网络协议栈自顶向下进行传输,通过传输层,网络层,数据链路层,最后通过硬件在网络中进行通信的
在之前的学习中,HTTP/HTTPS这些应用层协议是保证数据安全的,而现在所学的传输层是了保证数据能够可靠地传送到目标地址
2、端口号:
端口号标识了一个主机上进行通信的不同的应用程序
当一台主机接受到了数据,然后从下往上依次传输,但是这个数据应该是给哪个进程的,就由数据中的目的端口号来决定,这个操作是在传输层进行的,在传输层会提取出数据中的端口号,然后将数据给对应进程
端口号划分:
端口号的长度是16位,因此端口号的范围是0 ~ 65535:
0 ~ 1023:知名端口号。比如HTTP,FTP,SSH等这些广为使用的应用层协议,它们的端口号都是固定的
1024 ~ 65535:操作系统动态分配的端口号。客户端程序的端口号就是由操作系统从这个范围分配的
一个端口号是否可以被多个进程绑定?
一个端口号绝对不能被多个进程绑定,因为端口号的作用就是唯一标识一个进程,如果绑定一个已经被绑定的端口号,就会出现绑定失败的问题
一个进程是否可以绑定多个端口号?
一个进程是可以绑定多个端口号的,这与“端口号必须唯一标识一个进程”是不冲突的,只不过现在这多个端口唯一标识的是同一个进程罢了
我们限制的是从端口号到进程的唯一性,而没有要求从进程到端口号也必须满足唯一性,因此一个进程是可以绑定多个端口号的
3、五元组标识一个通信:
在TCP/IP协议中,用源IP,源端口号,目的IP,目的端口号,协议号这样一个五元组来标识一个通信
4、netstat
其作用是:查看网络状态
选项:
- n:拒绝显示别名,能显示数字的全部转换成数字
- l:仅列出处于LISTEN(监听)状态的服务
- p:显示建立相关链接的程序名
- t(tcp):仅显示tcp相关的选项
- u(udp):仅显示udp相关的选项
- a(all):显示所有的选项,默认不显示LISTEN相关
5、pidof:
通过进程名来查看服务器进程的 pid
二、UDP协议:
1、UDP协议格式:
对于UDP来说,无论发多大的数据,报头的大小是固定的,所以对UDP来说,将报头和有效载荷进行分离只需拿走前8个字节即可
报头解析:
源端口和目的端口:
当我们从一台主机向另一台主机发送你好的时候,你好就是UDP协议中的数据,然后操作系统就会自动给我们的数据添加报头,源端口就是发送方的端口号, 目的端口就是目标主机的端口号
客户端怎么知道源端口和目的端口的呢?
源端口号:对于客户端的端口号来说,这是随机的,在启动客户端的进程的时候,操作系统会从主机中空余的端口号随机一个进行使用
目的端口号:目的端口号,也就是服务端的端口号,服务端的端口号一般是不变的,并且同一个应用的客户端和服务端开发也是同一个团队,所以也就会知道服务端的端口号,它会在编写的时候就内置在客户端中,当客户端进行connect的时候,链接的就是这个内置的端口号
服务端怎么知道源端口和目的端口的呢?
客户端在发送数据前,会通过操作系统的套接字(Socket)接口创建连接,此时,操作系统会为客户端随机分配一个源端口号,并将其与用户指定的目的端口号(即服务端监听的端口)一起,封装到 TCP 报文的首部中,当服务端的传输层拿到报文后,进行报文和有效载荷分离,这样就能够拿到客户端封装在报文中的源端口和目的端口了
UDP长度和UDP检验和:
UDP长度:表示整个数据报(UDP首部+UDP数据)的长度,起作用是帮助我们知道当前数据是一个完整的报文,有了整个报文的长度, 有效载荷的长度直接使用报文长度减去8字节就行了
UDP检验和:UDP报文的检验和出错,就会直接将报文丢弃
如何理解报头:
Linux操作系统是通过C语言写的,对于协议来说,可以把它看作是一种约定,所以这就是一种C语言版本的struct结构体的位段类型(不是结构体)
位段和结构体
特性 | 结构体(Struct) | 位段(Bit Field) |
---|---|---|
内存分配 | 按字节 / 类型分配,可能有对齐 | 按位分配,成员可共享字节 |
内存效率 | 较低(可能有浪费) | 较高(适合小数据) |
适用场景 | 一般数据存储,注重直观性 | 节省内存场景(如标志、寄存器) |
访问限制 | 无特殊限制,可取地址 | 不可取地址,跨平台兼容性差 |
struct udp_header
{uint32_t src_port:16;uint32_t dst_port:16;uint32_t length:16;uint32_t check_code:16;
};
既然是类型,那么就可以定义变量,那么以后我们定义udp报头,其实就是创建一个这个类型的变量
2、面向数据报
应用层交给UDP多长的报文,UDP就原样发送,既不会拆分,也不会合并,这就叫做面向数据报
别人发了三个快递,那么我们就一定要收到三个,不会出现只收到一个,一个半这样的情况。如果只有一个包裹,那我们也不能只拿走一半
如果发送端调用一次sendto, 发送100字节, 那么接收端也必须调用对应的一次recvfrom, 接收100字节;而不能循环十次调用recvfrom, 每次接受10字节
结论:发送了一个报文,要么不读,要么 recvfrom 等到读取完一整个报文再返回
3、UDP缓冲区:
UDP没有真正意义上的发送缓冲区,因为它没有可靠机制,不需要把数据暂存起来,它直接调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作
UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃
UDP的socket既能读,也能写,因此UDP是全双工的
为什么UDP要有接收缓冲区:
临时存储已到达但尚未被应用程序读取的 UDP 数据包,相当于 “临时仓库”,让应用程序可以按照自己的节奏从缓冲区中读取数据,避免因处理速度不足导致的数据包丢失
如果 UDP 没有接收缓冲区,那么就要求上层及时将 UDP 获取到的报文读取上去,如果一个报文在 UDP 没有被读取,那么此时 UDP 从底层获取上来的报文数据就会被迫丢弃