目录
一、端口号
1. 五元组
2. 端口号范围划分
3. 一些知名端口号
4. 进程与端口号
5. 两个常用网络工具
5.1 netstat
5.2 pidof
二、UDP协议
1. udp协议格式
2. udp报文解包
3. udp报文分用
4. udp的特点
5. 缓冲区
5.1 tcp缓冲区
5.2 udp缓冲区
6. 一些常见的基于udp的应用层协议
一、端口号
1. 五元组
在一台主机中,是可以同时运行不同的应用程序的。而端口号则标识了一台主机上运行的不同的应用程序:
在上图中的主机A中就存在多个不同的应用程序。大家知道,一份数据从网络中传输过来后,需要通过网络协议栈自底而上传输。当数据到达传输层后,就需要通过数据携带的端口选择对应的应用程序。
在TCP/IP协议中,用“源IP”、“源端口号”、“目的IP”、“目的端口号”、“协议号”这样的五元组来标识一个通信。(可以通过“netstat -n”命令查看)。
这五元组中的前四个大家应该都很熟悉,前两个标识数据发送方,后两个标识数据接收方。至于协议号,是用于标识该数据使用的是何种协议。协议号其实是有点冗余的,因为协议号其实是可以用目的端口号替代的。但是协议号可以让这个五元组更加清晰的表征协议。即五元组合起来看,就是发送端和接收端用何种协议通信。
举个例子,例如大家在浏览器中打开多个网页,此时浏览器就会向服务器发起多次请求。在这些请求中,虽然 源IP、目的IP和目的端口号是相同的,但是源端口号是不同的,因为源端口号是由OS自动生成的。通过这种方式,服务器就能够准确的判断这些请求是否为不同请求。
2. 端口号范围划分
为了方便使用,在服务器中,有很多端口号都是固定的。
0~1023:知名端口号。如http、ftp、ssh等这些广为使用的应用层协议,它们的端口号都是固定的,属于这个范围内。
1024~65535:操作系统动态分配的端口号。客户端程序的端口号,就是由OS从这个范围分配的。换句话说,这个范围的端口号是可以让用户随意绑定分配的。
3. 一些知名端口号
在这里简单介绍几个知名端口号:
ssh服务器:使用22端口;
ftp服务器:使用21端口;
telnet服务器:使用23端口
http服务器:使用80端口;
https服务器:使用443端口;
除了这几个端口号外,还有一些其他知名端口号。大家可以执行“vim /etc/services”命令查看其他知名端口号。
在大家自己写应用程序绑定端口号时,最好避开0~1023范围的知名端口号。
4. 进程与端口号
大家知道,一个端口号对应一个进程,这就意味一个端口号只能被一个进程bind。但是这并不意味这一个进程只能bind一个端口号。事实上,一个进程是可以bind多个端口号的。
一个端口号被一个进程bind,是为了保持端口号->进程的唯一关系,即通过一个端口号找到唯一一个进程。
但是如果一个进程bind了多个端口号,这个条件依然没有被打破,可以通过该进程bind的任意一个端口号找到唯一进程。例如我们可以给一个应用程序bind两个端口号,一个端口号用于接收数据,一个端口号用于接收数据,这种方式都是可行的。
5. 两个常用网络工具
5.1 netstat
netstat是一个用于查看网络状态的工具。其常用选项有如下几个:
n:拒绝显示别名,将能够以数字显示的内容全部转化为数字
l:仅列出当前处于Listen(监听)状态的服务
p:显示建议相关连接的程序名
t(tup):仅显示使用tcp相关选项
u(udp):仅显示udp相关选项
a(all):显示所有选项,但默认不显示Listen相关的内容
使用方法很简单,在linux中输入“netstat -选项”即可:
5.2 pidof
pidof工具可以用于查看服务器的进程id。
之前大家应该都用过“ps axj | grep 进程名”的方式查看进程id。但如果仅仅是想查看某个服务器的进程的id,可以直接用“pidof 进程名”命令查看:
由此,我们可以再加上“xargs kill -9”来快速结束指定进程:
xargs可以将前面的命令的结果拼接到当前命令的后面。因此,上面"pid httServer | xargs kill -9"命令其实就相当于“kill -9 进程id”。
二、UDP协议
1. udp协议格式
udp报头的格式如下:
首先大家可以了解一下“数据”。数据其实就是我们在应用层中需要传输的数据。例如大家如果在客户端向服务器发送了一条“你好”消息,这条消息就是我们要传输的数据,这份数据到达传输层后,如果使用的是udp协议,那么就会为这份数据添加如上的报头。而数据本身,则被称为“有效载荷”。
这份报头中的16位源端口号和16位目的端口号大家应该都很清楚了,这里就不再多讲。
16位udp校验和,其实就是用于检测udp报文是否出错。在发送数据之前,会先对报文整体做校验。校验完后将校验值填入。当数据被接收后,对方会再次对报文做校验,如果结果不相等,那么说明报文中数据丢失,该份数据会被直接丢弃。
16位udp长度,标识了udp报文的长度,即报头+有效载荷。大家应该注意到了,这个udp长度是16位的,这就意味着它的最大长度是2^16,即65535字节,即64kb字节的数据。这就说明在udp中,一份报文的最大长度是64kb,如果要发送的数据超过了64kb,就需要程序员在应用层中自己将这份数据拆分为64kb以下的数据,分为多份发送。
2. udp报文解包
从上面的内容可以知道,当一份udp数据被发送后,这份数据中会带上udp报头。那么当这份数据被接收后,如何将这份数据的报头和报文相分离呢?udp中采用的方式就是“定长报头”,即报头有一个固定的长度,当需要解包时,直接将对应长度拿走即可。在udp中这个长度是8字节,因此当主机收到一份udp报文后,就可以直接去掉这份报文的前8个字节,后面的内容就是有效载荷。
那么我们如何拿到报头中的数据呢?大家知道,linux是用c写的,并且在以前的文章中也讲过,所谓的报头其实就是一份结构化数据。因此要拿到报头中的数据很简单,直接使用一个结构体,这个结构体中就存储了4个16字节的整形的成员变量:
通过这种方式,就可以很轻松的拿到报头中的数据了。
3. udp报文分用
udp报文解包完成后,就需要将有效载荷向上递交给应用层。那么udp报文如何知道应该把自己交付给应用层的那么程序呢?其实就是依赖于16位目的端口号。通过目的端口号,就可以让udp报文找到应递交的进程。
4. udp的特点
udp的特点主要有三个。
第一个是无连接。udp协议的特性就是只需要知道对端的ip和port就可以直接进行传输,不需要建立连接。如果大家了解了tcp就应该能很明显的感受到udp和tcp在连接上的差异。tcp需要客户端发起请求,服务器确认是否建立连接后才可以建立连接通信,而udp则无需这么做。
第二个是不可靠。在udp中没有“确认应答”和“超时重传”等用于维持可靠传输的机制。如果因为某些原因导致报文无法发送给对方,dup协议也不会给应用层返回任何错误消息。
第三个是面向数据报。面向数据报,大家可以看成发快递。例如你的朋友给你发快递,它可能发了1个,也可能发2个、3个。但无论他发多少个,你在拿快递时必定拿到的是一个完整的快递,而不是0.5、1.5这种不完整的快递。这其实就是数据报。
当客户端发送一份udp数据报后,服务器在读取这份报文后,只有两个选择,要么不读,要么就读走一份完整的报文,即必须“整发整取”。在udp中就无需考虑它读取或发送的是不是一个完整的请求,只需要考虑序列化和反序列的问题。
这就和tcp的面向字节流有很大差距。在面向字节流中,一份请求可能被分为多份发送,接收端接收数据时并不知道读取到的数据是否为一份完整的请求,需要程序员自行设计如何保证从字节流中提取到一个完整报文。
因此,面向数据报的缺点就是不能够灵活的控制读写数据的次数和数量。例如有一份100字节的数据,面向数据报在发送数据时发送了几次,接收端就必须要按照发送的次数来接收,即“发几次,收几次”;而面向字节流则可以分为多份接收,例如这100字节的数据分为了两次发,但是接收端可以每次读取10字节,分10次读取。
5. 缓冲区
5.1 tcp缓冲区
在了解udp缓冲区之前,我们要先了解tcp缓冲区,这样才能更好的理解udp缓冲区。
首先,我们使用的网络IO接口,如send、sendto、read等接口,它们本质上并不是发送和接收接口,而是拷贝接口。这就意味着,当调用这些接口时,并不是直接从网络中接收和发送数据,而是将下层数据拷贝到应用层的缓冲区中。
在传输层中,是存在接收缓冲区和发送缓冲区的。当应用层需要发送数据调用send、write等接口时,其实是将应用层的数据拷贝到传输层的“发送缓冲区”中,然后再向下通过网络发送给另一台主机。当接收数据时,数据通过网络被填入到传输层的“接收缓冲区”中,当我们调用recv、read等接口读取数据时,其实是将接收缓冲区中的数据拷贝到我们在应用层中准备好的缓冲区内,而非直接从网络接收数据。
通过这种方式,就让接收方和发送方同时拥有一组成对的发送缓冲区和接收缓冲区,就实现了通信双方在发送的数据的时候,也可以并发的接收数据,这就叫做“全双工”。
当应用层将数据拷贝给传输层后,应用层就可以直接返回去执行其他任务了。通过这样的方式,应用层就无需等待网络然后自己去发送和接收数据,提高了应用层的效率。这就好比我们要发快递时直接将快递交给快递站,让快递站帮我们发快递,无需我们自己去到目标地点把快递交给对方。通过这种方式就节省了我们很多的时间。同时在发快递时,快递的个数太少回不了本、快递在发送途中出现了意外等等问题都是让快递站去解决,无需我们自己想办法。应用层中也是如此,如何发送数据,要不要集中式发送数据,数据在传输中出现了意外等等问题,都交由传输层解决,应用层不用参与。
因此,tcp协议的译名是“传输控制协议”,即数据在发送过程中的一切情况,都由tcp去处理解决。
5.2 udp缓冲区
在udp中没有真正意义上的“发送缓冲区”,因为在udp中调用sendto后会直接将数据交给内核,由内核将数据传给网络层协议进行后续的传输动作,这就意味着udp并不需要发送缓冲区。
udp具有“接收缓冲区”。这个接收缓冲区可以用来存放网络中传来的数据。但是udp缓冲区并不能保证收到的udp数据报的顺序和发送udp数据报的顺序一致,即udp缓冲区中的数据可能是乱序的。而乱序也是不可靠的一种,因此udp中并没有处理这一情况的机制。
缓冲区的存在也降低了数据丢包的概率。如果udp中没有接收缓存区,那么如果进程正在处理一份数据,这份数据还没有处理完的时候又来一份数据,此时就无法收到该数据。但如果udp缓冲区满了,再到达的udp数据就会被丢弃。
由于udp中也是可以在发送数据的同时接收数据,因此udp也是“全双工”。
6. 一些常见的基于udp的应用层协议
NFS:网络文件系统;
TFTP:简单文件传输协议;
DHCP:动态主机配置协议;
BOOTP:启动协议(用于无盘设备启动);
DNS:域名解析协议;
在这几个协议中,为大家简单介绍一下DNS协议。在我们的浏览器中输入网址时,在https后面都带有一串www开头的字符串:
这个字符串其实就是域名。而这个域名被解析之后,其实就是一个ip地址。
大家可以在计算机上搜索cmd:
在这个界面中输入“ping 域名”命令:
通过这个命令,就可以直接看到该域名解析后对应的ip地址。