作者:@阿亮joy.
专栏:《学会Linux》
座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
传输层在网络层提供的不可靠服务之上,主要负责两台计算机之间的端到端的通信,为应用层提供可靠的数据传输服务。
端口号的作用
端口号(Port)标识了一个主机上进行网络通信的不同的应用程序,当主机从网络中获取数据时,数据需要自底向上进行交付,而上层存在多个应用程序,那么交付给哪一个应用程序就有端口号来决定。
五元组标识一个通信
在 TCP / IP 协议中, 用 “源IP”,“源端口号”,“目的IP”,“目的端口号” 和 “协议号” 这样一个五元组来标识一个通信。其中 IP 地址和端口号标识网络中唯一的一个进程,而协议号是一个整数,用于标识传输层使用的协议类型。常见的传输层协议包括 TCP 协议、UDP 协议等。
五元组可以通过 netstat 命令来查看
netstat -nltp #显示TCP连接
netstat -nlup #显示UDP连接
有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:
执行下面的命令,可以看到知名端口号。
cat /etc/services
vim /etc/services
sshd 是 Secure Shell Daemon 的缩写,它是 OpenSSH 服务器的守护进程,其端口号是 22。
一个进程可以绑定多个端口号吗?
一个进程可以绑定多个端口号。因为在计算机网络中,一个进程可能需要提供不同的服务或处理不同类型的数据流,因此需要监听不同的端口来处理来自网络的不同请求。
例如:一个 Web 服务器进程可能需要同时监听 HTTP 和 HTTPS 协议的请求,因此需要绑定80和443端口。
一个端口号可以被多个进程绑定吗?
通常情况下,一个端口号只能被一个进程绑定。这是因为端口号用于标识网络上的特定服务,当客户端尝试连接到服务器的某个端口时,操作系统需要知道哪个进程负责处理该连接请求。如果多个进程绑定到同一个端口号,操作系统将无法确定应该将连接请求发送给哪个进程。因此,当一个进程试图绑定到已经被另一个进程占用的端口时,操作系统通常会返回错误。
但是如果采取的通信协议不同,就可以绑定同一个端口号。例如:一个进程可以使用 TCP 协议绑定到端口号 80,而另一个进程可以使用 UDP 协议绑定到端口号 80。这是因为操作系统不仅根据端口号,还根据通信协议来区分不同的服务。因此,当客户端尝试连接到服务器的某个端口时,操作系统会根据客户端使用的通信协议来确定应该将连接请求发送给哪个进程。
某个报文经过网络传输到达了指定的计算机,它是如何交给指定的进程处理的呢?
当进程调用 accept 函数获取到一个新连接后,会返回一个文件描述符。那么一个连接相当于就是一个文件,而接收到报文就相当于将数据放入到文件的接收缓冲区中,此时进程就收到了这些数据了。那么现在的问题就转换成如何通过进程绑定的端口号来找到进程了。
当一个报文到达指定的计算机时,它首先会被网络协议栈处理。网络协议栈会根据报文中的信息,如目标 IP 地址和目标端口号,来确定报文应该交给哪个进程处理。操作系统会维护一个端口号到进程的映射表,当网络协议栈确定了报文应该交给哪个端口号时,它会查询这张映射表,找到对应的进程,然后将报文传递给该进程进行处理。
netstat 是一个用来查看网络状态的重要工具。
语法: netstat [选项]
功能:查看网络状态
常用选项:
pidof 命令用于查找指定名称进程的进程 ID。
pidof HttpServer | xargs kill -9
命令的作用是强制杀死名为 HttpServer 的进程。首先,pidof HttpServer 命令会查找名为HttpServer 的进程的进程 ID。然后,这些进程 ID 会通过管道传递给 xargs kill -9命令。xargs 命令会将接收到的进程 ID 作为参数传递给 kill -9 命令。最后,kill -9 命令会向这些进程发送 SIGKILL 信号,强制杀死这些进程。
xargs 命令是一个给命令传递参数的过滤器,也是组合多个命令的一个工具。它可以将管道或标准输入(stdin)数据转换成命令行参数传递给后一个命令,也能够从文件的输出中读取数据。xargs还可以将单行或多行文本输入转换为其他格式,例如多行变单行,单行变多行
将文件的时间更为最新时间
ls | xargs touch
将当前目录下文件名中包含 “foo” 的所有文件删除
find . -name "*foo*" | xargs rm -rf
UDP 报文分为 UDP 报头和 UDP 数据区两部分。报头由 4 个 16 位长(2字节)字段组成,分别说明该报文的源端口、目的端口、报文长度和校验值。
UDP 是如何将报头和有效载荷进行分离或如何对有效载荷进行封装,以及如何将有效载荷交给上层呢?
由于 UDP 报头中包含了源端口、目的端口、报文长度和校验值等信息,网络协议栈可以通过这些信息来分离报头和有效载荷。具体来说,网络协议栈会根据报文长度字段的值来确定 UDP 报文头和有效载荷的边界。
在分离出有效载荷后,网络协议栈会将其传递给上层应用程序进行处理。操作系统会维护一个端口号到进程的映射表,当网络协议栈确定了数据报应该交给哪个端口号时,它会查询这张映射表,找到对应的进程,然后将有效载荷传递给该进程进行处理。
为什么应用层使用的端口号类型是 uint16_t 呢?
应用层使用的端口号类型是 uint16_t 是因为在 UDP 和 TCP 报文头中,源端口和目的端口字段都是 16 位长。这意味着端口号的取值范围是 0 到 65535。uint16_t 类型正好可以表示这个范围内的所有整数,因此它被用作应用层端口号的类型。
理解 UDP 报文本身
UDP 协议是一种无连接的、不可靠、面向数据报的传输协议。
为什么说 UDP 协议是面向数据报的呢?
UDP 协议是面向数据报的,这意味着它将应用层传递给它的数据作为一个独立的数据报来处理。每个数据报都包含了足够的信息,使得接收端能够将其独立地传递给上层应用程序。
在 UDP 协议中,应用层交给 UDP 多长的报文,UDP 原样发送,既不会拆分,也不会合并。这意味着,如果发送端调用一次 sendto 发送 100 个字节,那么接收端也必须调用一次 recvfrom 接收 100 个字节;而不能循环接收 10 次,每次接收 10 个字节。
由于 UDP 协议简单、高效,它仍然被广泛用于实时应用、多播和广播等场景。
sendto、recvfrom、send、recv、write 和 read 等 IO 类接口都是用于在应用程序和操作系统内核之间传输数据的系统调用。它们的本质是在用户空间和内核空间之间拷贝数据。
例如,当应用程序调用 sendto 函数发送数据时,它会将数据从用户空间拷贝到内核空间,然后由内核将数据发送到网络。同样,当应用程序调用 recvfrom 函数接收数据时,内核会将接收到的数据从内核空间拷贝到用户空间,供应用程序使用。
除了拷贝数据之外,这些 IO 类接口还会执行其他操作,如检查套接字状态、设置套接字选项、处理错误等。因此,它们不仅仅是简单的拷贝函数。
UDP的缓冲区
UDP的全双工
UDP 协议支持全双工通信,这意味着它可以同时进行发送和接收操作。也就是说,应用程序可以在不等待接收完成的情况下发送数据,反之亦然。这种全双工的工作方式提高了通信效率,使得 UDP 协议能够更好地满足实时应用的需求。
UDP 协议首部中有一个 16 位的最大长度,也就是说一个 UDP 能传输的数据最大长度是 64K(包含 UDP 首部)。然而 在当今的互联网环境下,64 K 是一个非常小的数字,如果我们需要传输的数据超过 64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装。
当然,也包括你自己写 UDP 程序时自定义的应用层协议。
本篇博客主要讲解了端口号划分、知名端口号、nestat 和 pidof 指令、UDP 协议端格式、UDP 的特点、UDP 协议的使用注意事项以及基于 UDP 的应用层协议等等。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!❣️