「前言」文章内容大致是传输层协议,UDP协议讲解。
「归属专栏」网络编程
「主页链接」个人主页
「笔者」枫叶先生(fy)
目录
- 一、传输层
- 二、UDP协议
- 2.1 再谈端口号
- 2.2.1 端口号范围划分
- 2.2.2 认识知名端口号
- 2.2.3 端口号注意问题
- 2.2.4 netstat命令和pidof命令
- 2.2 UDP协议格式
- 2.3 UDP的特点
- 2.4 UDP的缓冲区
- 2.5 UDP注意事项
- 2.6 基于UDP的应用层协议
一、传输层
HTTP协议普通用户认为是将请求和响应直接发送到了网络当中。但实际应用层需要先将数据交给传输层,由传输层对数据做进一步处理后再将数据继续向下进行交付,该过程贯穿整个网络协议栈,最终才能将数据发送到网络当中
传输层负责在网络中提供可靠的数据传输服务。它主要解决了主机之间的通信问题。
常见的传输层协议有TCP(传输控制协议)和UDP(用户数据报协议),传输层已经是操作系统内核部分了
下面学习的是UDP协议,TCP在下一篇。
二、UDP协议
2.1 再谈端口号
重谈概念
端口号是在传输层中使用的一个标识符,用于识别不同的应用程序或服务。它是一个16位的整数(2字节),范围从0到65535
- 端口号的组合由IP地址和端口号一起构成了一个套接字(Socket),用于唯一标识网络中的通信终点。
- 在传输层的通信中,源主机使用源端口号,目标主机使用目标端口号,以便正确地将数据传输到相应的应用程序或服务。
- 通过使用不同的端口号,传输层可以实现多个应用程序同时进行通信,保证数据的正确传输和接收。
端口号的作用
- 端口号(Port)标识一个主机上进行网络通信的不同的应用程序,即标识主机上进程的唯一性
- 从网络中获取的数据在进行向上交付时,在传输层就会提取出该数据对应的目的端口号,进而确定该数据应该交付给当前主机上的哪一个服务进程
- 即在传输层协议的报头当中就会包含与端口相关的字段
这些概念在套接字篇章都谈过了,不再赘述
五元组
在TCP/IP协议中,用“源IP地址”,“源端口号”,“目的IP地址”,“目的端口号”,“协议号”这样一个五元组来标识一个通信。
比如有多台客户端主机同时访问服务器,这些客户端主机上可能有一个客户端进程,也可能有多个客户端进程,它们都在访问同一台服务器。
而这台服务器就是通过“源IP地址”,“源端口号”,“目的IP地址”,“目的端口号”,“协议号”来识别一个通信的。
2.2.1 端口号范围划分
端口号的长度是16位,因此端口号的范围是0 ~ 65535
:
- 已知端口号(
Well-known Ports
)是指被分配给特定服务或协议的端口号,范围从0到1023 - 动态端口号(
Dynamic Ports
)是指在使用过程中临时分配给应用程序的端口号,范围从1024到65535。 0 ~ 1023
:知名(已知)端口号。比如HTTP,FTP,SSH等这些广为使用的应用层协议,它们的端口号都是固定的。1024 ~ 65535
:操作系统动态分配的端口号。客户端程序的端口号就是由操作系统从这个范围分配的。
2.2.2 认识知名端口号
有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号:
- ssh服务器,使用
22
端口。 - ftp服务器,使用
21
端口。 - telnet服务器,使用
23
端口。 - http服务器,使用
80
端口。 - https服务器,使用
443
端口。
查看知名端口号:
vim /etc/services
我们自己写一个程序使用端口号时,要避开这些知名端口号
2.2.3 端口号注意问题
一个进程是否可以绑定多个端口号?
- 一个进程可以绑定多个端口号。这与“端口号必须唯一标识一个进程”是不冲突的
- 在某些情况下,一个进程可能需要同时提供多个服务或协议,或者需要同时监听多个端口来处理不同类型的数据
一个端口号是否可以被多个进程绑定?
- 一个端口号通常只能被一个进程绑定(%99的情况下都是)。每个端口号在一个特定的时间点只能由一个进程使用,以确保数据的正确传输和接收
- 如果绑定一个已经被绑定的端口号,就会出现绑定失败的问题
2.2.4 netstat命令和pidof命令
netstat命令
netstat
是一个用于显示网络连接、路由表和网络接口信息的命令
netstat
英文全称:network statistics
网络统计
常用选项:
-a
:all (显示所有连接和监听端口)-t
:tcp (仅显示TCP连接)-u
:udp (仅显示UDP连接)-n
:numeric (以数字形式显示IP地址和端口号)-p
:program (显示与连接关联的进程信息)-l
:listen(仅列出有在 Listen (监听) 的服务状态)-r
:route (显示路由表信息)-s
:statistics (显示网络统计信息)
命令演示
查看TCP相关的网络信息时,一般选择使用-nltp
组合选项。
netstat -nltp
查看UDP相关的网络信息时,一般选择使用-lnup
组合选项。
netstat -lunp
pidof命令
pidof
命令是一个用于查找正在运行的进程的命令。它可以通过进程名来查找与之匹配的进程的进程ID(PID),比较方便
语法: pidof [进程名]
功能:通过进程名,查看进程id
命令演示
可以组合使用
pidof test | xargs kill -9
注:xargs
是一个用于构建和执行命令行的实用程序。它从标准输入中读取数据,并将其作为参数传递给指定的命令
2.2 UDP协议格式
UDP协议格式如下:
前8个字节是UDP的报头,报头包括:
- 16位源端口号:表示数据从哪里来。
- 16位目的端口号:表示数据要到哪里去。
- 16位UDP长度:表示整个数据报(UDP首部+UDP数据)的长度,即UDP的报文最大长度是
64KB
。 - 16位UDP检验和:如果UDP报文的检验和出错,就会直接将报文丢弃。
数据部分(用户数据)可有可无,数据部分就是有效载荷
UDP如何将报头与有效载荷进行分离?
UDP的报头已经规定大小,即UDP采用定长报头,UDP在读取报文时读取完前8个字节后剩下的就都是有效载荷
UDP如何决定将有效载荷交付给上层的哪一个协议?
- UDP是通过报头当中的目的端口号来找到对应的应用层进程的,即由目的端口号决定。
- 当UDP数据报到达时,操作系统会根据目的端口号找到相应的应用程序或服务,并将数据报交付给它进行处理
理解协议的报头
Linux内核是用C语言写的,UDP/TCP协议处于内核中,所以所谓的报头就是一种结构化的数据对象
比如UDP,下图左边是位段写法
数据封装:
以UDP为例,应用层将数据交给传输层时,传输层会创建一个UDP报头,并填充报头中的各个字段,包括源端口号和目的端口号等信息。然后,操作系统会在内核中开辟一块空间,将UDP报头和有效载荷(即应用层数据)拷贝到一起,形成UDP报文
分用
以UDP为例,当传输层从下层获取到一个报文后,就会读取该报文的前8个字节,提取出对应的目的端口号。通过目的端口号找到对应的上层应用层进程,然后将剩下的有效载荷向上交付给该应用层进程
2.3 UDP的特点
UDP传输的过程就类似于寄信,其特点如下:
- 无连接:知道对端的IP和端口号就直接进行数据传输,不需要建立连接。
- 不可靠:没有确认机制,没有重传机制;如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息。
- 面向数据报:不能够灵活的控制读写数据的次数和数量。
无连接,在套接字代码已经体现到了(与TCP相比),不解释;不可靠学了TCP就懂了
下面解释一下数据报
面向数据报
- 应用层交付给UDP多长的报文,UDP就原样发送,既不会拆分,也不会合并,这就叫做面向数据报。
- 可以想像成快递:你朋友给你发了1个快递,你就只能收1个快递,你不能只收0.5个快递,也不能收2个快递,你只能收1个,即发多少就收多少。对比读数据
- UDP发送的数据不能太大,UDP的报文最大长度是64KB(UDP首部+UDP数据)
- 如果超过 64KB了,必须进行拆分,拆分的每个小数据都必需比64KB小,这个是应用层写代码自己做的工作。如果超了,就发不了
- 比如用UDP传输100个字节的数据,发送端调用一次发送函数,发送100字节,那么接收端也必须调用对应的一次接收函数,接收100个字节;如果发送端调用十次发送函数,则接收端也必须调用对应的十次接收函数,即UDP协议,发送函数的次数 : 接收函数的次数 = 1 : 1
2.4 UDP的缓冲区
- UDP没有真正意义上的发送缓冲区。调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作。
- UDP具有接收缓冲区。但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃。
缓冲区以TCP为例
发送接收数据:
- 发送函数(write等)在自己的应用层有自己的应用层发送缓冲区,调用发送函数实际上是把数据拷贝到传输层的发送缓冲区中
- 数据是否发送到网络中,由TCP协议自主决定,所以TCP协议称为传输控制协议,关键字:传输控制
- 接收函数(read等)在自己的应用层也有自己的应用层接收缓冲区,调用接收函数实际上是把传输层接受缓冲区中数据拷贝到自己应用层接收缓冲区中
- 所以
read、write、send、recv、sendto
等函数本质上是拷贝函数 - 一方在发送,另一方也在发送,双方根本就不会影响,因为它们有成对的缓冲区,一个负责发送,一个负责接收,所以TCP是全双工的
- 这对缓冲区有生产消费模型的思想,兼具该模型的优点
为什么UDP没有发送缓冲区
- 因为不需要,UDP协议的设计目标是提供一种简单的、无连接的通信方式。
- UDP协议的设计初衷是为了实现高效的数据传输,尽量减少协议本身的开销。
- UDP协议并不提供可靠性和流量控制的机制,因此不需要发送缓冲区
- 调用sendto会把数据直接交给内核,由内核将数据传给网络层协议进行后续的传输动作。
为什么UDP要有接收缓冲区?
如果UDP没有接收缓冲区,那么就要求上层及时将UDP获取到的报文读取上去,如果一个报文在UDP没有被读取,那么此时UDP从底层获取上来的报文数据就会被迫丢弃。
注意:UDP接收缓冲区满了,再来报文直接丢弃
UDP需要有接收缓冲区是为了应对网络传输中的不可靠性和不确定性。
因为UDP本身是不可靠的,所以接收方可能会面临以下情况:
- 数据到达的速度快于应用程序的处理速度:如果接收方的应用程序无法及时处理到达的数据报,那么接收缓冲区可以暂时存储这些数据,避免数据丢失。
- 数据到达的顺序可能不同于发送顺序:由于UDP的无连接性,数据报可能以不同的顺序到达接收方。接收缓冲区可以暂时存储这些乱序的数据报,并按照应用程序的需要进行排序和组装。
- 网络拥塞或丢包:在网络传输中,可能会发生丢包或拥塞的情况。接收缓冲区可以缓冲部分数据,以便在网络恢复正常后重新接收丢失的数据。
虽然UDP没有发送缓冲区,但是UDP的socket既能读,也能写,所以也是全双工
2.5 UDP注意事项
上面已经谈过了
- 需要注意的是,UDP协议报头当中的UDP最大长度是16位的,因此一个UDP报文的最大长度是64K(包含UDP报头的大小)。
- 然而64K在当今的互联网环境下,是一个非常小的数字。如果需要传输的数据超过64K,就需要在应用层进行手动分包,多次发送,并在接收端进行手动拼装。
2.6 基于UDP的应用层协议
- NFS:网络文件系统。
- TFTP:简单文件传输协议。
- DHCP:动态主机配置协议。
- BOOTP:启动协议(用于无盘设备启动)。
- DNS:域名解析协议。
--------------------- END ----------------------
「 作者 」 枫叶先生
「 更新 」 2023.7.18
「 声明 」 余之才疏学浅,故所撰文疏漏难免,
或有谬误或不准确之处,敬请读者批评指正。