文章目录
- Ⅰ. 传输层概述
- 1、进程之间的通信
- 2、再谈端口号
- 端口号的引出
- 五元组标识一个通信
- 端口号范围划分
- 常见的知名端口号
- 查看知名端口号
- 协议号 VS 端口号
- 3、两个问题
- 一个端口号是否可以被多个进程绑定?
- 一个进程是否可以绑定多个端口号?
- 4、部分常见指令
- pidof(用于查看进程id)
- netstat(查看网络状态)
- Ⅱ. UDP协议
- 1、UDP协议所处位置
- 2、UDP协议格式
- 如何理解首部?
- UDP协议如何将首部与有效载荷进行分离?
- UDP协议如何决定将有效载荷交付给上层的哪一个协议?
- 3、UDP的主要特点
- 4、面向数据报
- 5、UDP协议的缓冲区
- 为什么UDP要有接收缓冲区?
- 6、基于UDP的应用层协议

Ⅰ. 传输层概述
1、进程之间的通信
在学习 HTTP
等应用层协议时,为了便于理解,可以简单的认为 HTTP
协议是将请求和响应直接发送到了网络当中。但实际应用层需要先将数据交给传输层,再由传输层对数据做进一步处理后再将数据继续向下进行交付,该过程贯穿整个网络协议栈,最终才能将数据发送到网络当中。
传输层负责可靠性传输,确保数据能够可靠地传送到目标地址。从通信和信息处理的角度看,运输层为它上面的应用层提供通信服务!
但是我们要明确的是,真正进行通信的实体,其实是主机中的应用进程,也就是进程之间的通信!而为了达到传输层向上服务应用层的时候,可以找到对应的进程,就有了两个重要的功能:复用和分用!如下图所示:
此外,传输层还要对收到的报文进行差错检测,只不过 传输层只检验首部是否出现差错,而不检查有效载荷部分!
2、再谈端口号
端口号的引出
应用层所有的应用进程都可以通过运输层再传送到网络层,这叫做复用;而运输层从网络层收到发送给各应用进程的数据后,必须分别交付指明的各应用进程,这就是分用。
所以很明显,给应用层的每个应用进程赋予一个非常明确唯一的标志是至关重要的!
我们知道,一台计算机中的进程有着唯一标识的进程标识符,但因为这个进程标识符和操作系统是强相关的,如果我们将其作为复用和分用的标识的话,那么会有一个问题,就是一个进程的标识符由操作系统管理,所以进程的销毁和创建都会导致进程标识符的变化,但是我们又是希望在网络中传输的时候,对方的唯一标志可以是尽量保持不变的(因为一旦变化,请求连接的标识也需要改变),所以我们 不能用进程标识符作为复用和分用的唯一标志!
所以此时就有人使用了端口号这种方案来作为唯一标识!
端口号(Port
)标识一个主机上进行网络通信的不同的应用程序。当主机从网络中获取到数据后,需要自底向上进行数据的交付,而这个数据最终应该交给上层的哪个应用处理程序,就是由该数据当中的目的端口号来决定的。
从网络中获取的数据在进行向上交付时,在传输层就会提取出该数据对应的目的端口号,进而确定该数据应该交付给当前主机上的哪一个服务进程。
因此端口号是属于传输层的概念的,在传输层协议的报头当中就会包含与端口相关的字段。
请注意,端口号只具有本地意义,在互联网不同的计算机中,相同的端口号是没有关联的!
五元组标识一个通信
在 TCP/IP
协议中,用 “源IP地址”、“源端口号”、“目的IP地址”、“目的端口号”、“协议号” 这样一个五元组来标识一个通信。
比如有多台客户端主机同时访问服务器,这些客户端主机上可能有一个客户端进程,也可能有多个客户端进程,它们都在访问同一台服务器。
而这台服务器就是通过 “源IP地址”、“源端口号”、“目的IP地址”、“目的端口号”、“协议号” 来识别一个通信的。
- 先提取出数据当中的目的
IP
地址和目的端口号,确定该数据是发送给当前服务进程的。 - 然后提取出数据当中的协议号,为该数据提供对应类型的服务。
- 最后提取出数据当中的源
IP
地址和源端口号,将其作为响应数据的目的IP地址和目的端口号,将响应结果发送给对应的客户端进程。
通过 netstat
命令可以查看到这样的五元组信息。
其中的 Local Address
表示的就是源IP地址和源端口号,Foreign Address
表示的就是目的IP地址和目的端口号,而 Proto
表示的就是协议类型。
端口号范围划分
端口号的长度是 16
位,因此端口号的范围是 0 ~ 65535
:
0 ~ 1023
:知名端口号。比如HTTP
、FTP
、SSH
等这些广为使用的应用层协议,它们的端口号都是固定的。1024 ~ 65535
:操作系统动态分配的端口号。客户端程序的端口号就是由操作系统从这个范围分配的。
常见的知名端口号
有些服务器是非常常用的,这些服务器的端口号一般都是固定的:
ssh
服务器,使用22
端口ftp
服务器,使用21
端口telnet
服务器,使用23
端口dns
服务器,使用53
端口http
服务器,使用80
端口https
服务器,使用443
端口
查看知名端口号
我们可以查看 /etc/services
文件,该文件是记录网络服务名和它们对应使用的端口号及协议。
说明一下:文件中的每一行对应一种服务,它由 4
个字段组成,每个字段之间用 TAB
或空格分隔,分别表示“服务名称”、“使用端口”、“协议名称”以及“别名”。
协议号 VS 端口号
- 协议号是存在于
IP
报头当中的,其长度是8
位。协议号指明了数据报所携带的数据是使用的何种协议,以便让目的主机的IP
层知道应该将该数据交付给传输层的哪个协议进行处理。 - 端口号是存在于
UDP
和TCP
报头当中的,其长度是16
位。端口号的作用是唯一标识一台主机上的某个进程。 - 协议号是作用于传输层和网络层之间的,而端口号是作用于应用层于传输层之间的。
3、两个问题
一个端口号是否可以被多个进程绑定?
一个端口号绝对不能被多个进程绑定,因为端口号的作用就是唯一标识一个进程,如果绑定一个已经被绑定的端口号,就会出现绑定失败的问题。
一个进程是否可以绑定多个端口号?
一个进程是可以绑定多个端口号的,这与“端口号必须唯一标识一个进程”是不冲突的,只不过现在这多个端口唯一标识的是同一个进程罢了。
我们限制的是从端口号到进程的唯一性,而没有要求从进程到端口号也必须满足唯一性,因此一个进程是可以绑定多个端口号的。
4、部分常见指令
pidof(用于查看进程id)
pidof httpServer | xargs kill -9 // xargs用于将管道前面获取的参数追加到该指令后面
netstat(查看网络状态)
n 拒绝显示别名,能显示数字的全部转化成数字
l 仅列出有在 Listen (监听) 的服务状态
p 显示建立相关链接的程序名
t(tcp) 仅显示tcp相关选项
u(udp) 仅显示udp相关选项
a(all) 显示所有选项,默认不显示LISTEN相关
Ⅱ. UDP协议
1、UDP协议所处位置
网络套接字编程时用到的各种接口,是位于应用层和传输层之间的一层系统调用接口,这些接口是系统提供的,我们可以通过这些接口搭建上层应用,比如 HTTP
。我们经常说 HTTP
是基于 TCP
的,实际就是因为 HTTP
在 TCP
套接字编程上搭建的。
而 socket
接口往下的传输层实际就是由操作系统管理的,因此 UDP
是属于内核当中的,是操作系统本身协议栈自带的,其代码不是由上层用户编写的,UDP
的所有功能都是由操作系统完成,因此网络也是操作系统的一部分。
2、UDP协议格式
其格式如下:
16
位源端口号:表示数据从哪里来。16
位目的端口号:表示数据要到哪里去。16
位整个报文长度:表示整个数据报(UDP
首部 +UDP
数据)的长度。- 这也就说明
UDP
最大可传输的长度为2^16
即64KB
(包含首部字段),如果需要 传输大于64KB
的数据,就需要应用层多次分包,并在接收端手动拼装。
- 这也就说明
16
位检验和:如果UDP
报文的检验和出错,就会直接将报文丢弃。
之所以我们在应用层看到的端口号大部分都是 16
位的,比如使用 uint16_t
类型,其根本原因就是因为传输层协议当中的端口号就是 16
位的。
如何理解首部?
操作系统是 C
语言写的,而 UDP
协议又是属于内核协议栈的,因此 UDP
协议也一定是用 C
语言编写的,所以 UDP
首部本质上是一个结构体对象或者包含位段的结构体,如下所示:
struct udphdr {uint16_t uh_sport; /* 源端口号 */uint16_t uh_dport; /* 目的端口号 */uint16_t uh_ulen; /* UDP数据报长度(包括头部+数据) */uint16_t uh_sum; /* 数据校验和 */
};
UDP数据封装:
- 当应用层将数据交给传输层后,在传输层就会创建一个
UDP
报头类型的变量,然后填充报头当中的各个字段,此时就得到了一个UDP
报头。 - 此时操作系统再在内核当中开辟一块空间,将
UDP
报头和有效载荷拷贝到一起,此时就形成了UDP
报文。
UDP数据分用:
- 当传输层从下层获取到一个报文后,就会读取该报文的前
8
个字节,提取出对应的目的端口号。 - 通过目的端口号找到对应的上层应用层进程,然后将剩下的有效载荷向上交付给该应用层进程。
UDP协议如何将首部与有效载荷进行分离?
UDP
的报头当中只包含四个字段,每个字段的长度都是 16
位,总共 8
字节。因此 UDP
采用的实际上是一种定长报头,UDP
在读取报文时读取完前 8
个字节后剩下的就都是有效载荷了。
而我们只需要提取报头中的 数据报长度 字段,减去 8
个字节,最后的大小就是有效载荷的长度,是可以直接读取上来的!
UDP协议如何决定将有效载荷交付给上层的哪一个协议?
UDP
上层也有很多应用层协议,因此 UDP
必须想办法将有效载荷交给对应的上层协议,也就是交给应用层对应的进程。
应用层的每一个网络进程都会绑定一个端口号,服务端进程必须显示绑定一个端口号,客户端进程则是由系统动态绑定的一个端口号。UDP
就是通过报头当中的目的端口号来找到对应的应用层进程的。
说明一下: 内核中用哈希的方式维护了端口号与进程
ID
之间的映射关系,因此传输层可以通过端口号得到对应的进程ID
,进而找到对应的应用层进程。
3、UDP的主要特点
- 无连接。发送数据之前不需要建立连接。
- 使用尽最大努力交付。即 不保证可靠交付。
- 面向报文。
UDP
一次传送和交付一个完整的报文。 - 没有拥塞控制。网络出现的拥塞不会使源主机的发送速率降低。很适合多媒体通信的要求。
- 支持一对一、一对多、多对一、多对多等交互通信。
- 首部开销小,只有
8
个字节。
总结一点,就是【简单方便,但不可靠】。
此外,应用程序必须选择合适大小的报文。若报文太长,IP
层在传送时可能要进行分片,降低 IP
层的效率。若报文太短,会使 IP
数据报的首部的相对长度太大,降低 IP
层的效率。
4、面向数据报
应用层交给 UDP
多长的报文,UDP
就原样发送,既不会拆分,也不会合并,这就叫做面向数据报。
比如用 UDP
传输 100
个字节的数据:
如果发送端调用一次 sendto
,发送 100
字节,那么接收端也必须调用对应的一次 recvfrom
,接收 100
个字节,而不能循环调用 10
次 recvfrom
去每次接收 10
个字节。
5、UDP协议的缓冲区
UDP
协议不需要考虑多个报文之间的粘连问题,因为 UDP
没有真正意义上的发送缓冲区。调用 sendto
函数会直接交给内核(应用层发一个,传输层送走一个),由内核将数据传给网络层协议进行后续的传输动作。
但是 UDP
具有接收缓冲区。但是这个接收缓冲区不能保证收到的 UDP
报的顺序和发送 UDP
报的顺序一致。如果缓冲区满了,再到达的 UDP
数据就会直接被丢弃,所以 UDP
协议是一种不可靠、全双工的协议。
为什么UDP要有接收缓冲区?
如果 UDP
没有接收缓冲区,那么就要求上层及时将 UDP
获取到的报文读取上去,如果一个报文在 UDP
没有被读取,那么此时 UDP
从底层获取上来的报文数据就会被迫丢弃。
一个报文从一台主机传输到另一台主机,在传输过程中会消耗主机资源和网络资源。如果 UDP
收到一个报文后仅仅因为上次收到的报文没有被上层读取,而被迫丢弃一个可能并没有错误的报文,这就是在 浪费主机资源和网络资源。
因此 UDP
本身是会维护一个接收缓冲区的,当有新的 UDP
报文到来时就会把这个报文放到接收缓冲区当中,此时上层在读数据的时就直接从这个接收缓冲区当中进行读取就行了,而如果 UDP
接收缓冲区当中没有数据那上层在读取时就会被阻塞。因此 UDP
的接收缓冲区的作用就是,将接收到的报文暂时的保存起来,供上层读取。
6、基于UDP的应用层协议
NFS
:网络文件系统TFTP
:简单文件传输协议DHCP
:动态主机配置协议BOOTP
:启动协议(用于无盘设备启动)DNS
:域名解析协议
当然,也包括自己写 UDP
程序时自定义的应用层协议。