Linux系统编程:UDP协议和TCP协议

news2024/11/27 12:54:42

目录

一. 对于端口号的理解

1.1 网络通信五元组

1.2 端口号的划分策略

二. 网络通信中常用的指令

2.1 netstat指令

2.2 pidof指令

三. udp协议

3.1 udp的概念及特点

3.2 udp协议端格式

3.3 对于面向数据报及应用层发送与读取数据的理解

四. tcp协议的概念及特点

五. tcp报头

5.1 tcp协议端格式

5.2 tcp报头各部分的含义

六. 三次握手和四次挥手

6.1 三次握手和四次挥手的流程

6.2 三次握手解析

6.3 四次挥手解析

七. tcp协议保证可靠性和提高效率的机制

7.1 确认应答机制

7.2 超时重传机制

7.3 去重和按序到达机制

7.4 流量控制机制

7.5 滑动窗口机制

7.6 快重传机制

7.7 网络拥塞控制机制

7.8 延时应答机制

7.9 捎带应答机制

八. 对listen接口的第二个参数的理解

九. 对于tcp一些概念的理解

9.1 面向字节流

9.2 粘包问题

9.3 tcp异常情况

9.4 udp实现可靠通信的方法

十. 总结


一. 对于端口号的理解

1.1 网络通信五元组

在网络通信中,假设主机A要给主机B的发送数据,那么本质上就是主机A的某个进程要与主机B的某个进行实现进程间通信。主机A发数据时,通过全网中唯一的IP地址,可以定位到主机B,再通过目的端口号,即可获取主机B上要接受主机A发送信息的进程。通过 IP地址 + 端口号,可以确定全网中唯一的一个进程,实现网络环境下的进程间通信。

网络通信五元组:源IP、目的IP、源端口号、目的端口号、协议号。

这里的协议号,我们可以理解为传输层所使用的协议,即:udp(面向数据报协议)或tcp(传输控制协议)。

如图1.1所示,假设两个客户端都打开了某一浏览器,那么客户端就要与服务器建立通信。对于服务器而言,需要属于其自身的独立端口号,以便客户端确定接受信息的进程。而客户端的可能打开多个浏览器画面,每个浏览器画面都是一个独立的进程,拥有独立的端口号。

在图1.1中,服务器就是目的端,其ip和提高服务的进程的端口号,就是目的端口号。而客户端的ip和打开浏览器界面的进程的端口号,就是源端口号。传输层使用的tcp/udp协议,可以理解为协议号。

一个进程可以绑定多个端口号,但一个端口号不能绑定多个进程。

图1.1 端口号在通信中的应用

1.2 端口号的划分策略

端口号用16位的数据表示,范围是0~65535,根据编号数值,将端口号划分为知名端口号和操作系统动态分配的端口号:

  • 0 ~ 1023:知名端口号。一些知名的服务,如http、https、ssh等使用的端口号,这些知名服务使用的端口号是固定的,这样能够方便客户端进行连接。
  • 1024 ~ 65535:由操作系统动态分配的端口号,应用层软件的进程,所使用的的都是这些端口号,如果使用知名端口号则很容易引发冲突。

常见的知名端口号:http(80)、https(443)、ftp(21)、ssh(22)、telnet(23)。

二. 网络通信中常用的指令

2.1 netstat指令

netstat指令的功能为查看OS中的网络状态。

语法:netstat [选项]

netstat常见选项及功能:

  • n:尽量以数字的形式显示网络信息。
  • t:显示使用tcp协议的网络通信。
  • u:显示使用udp协议的网络通信。
  • l:显示处于监听状态的服务器。
  • p:显示进行网络通信的进程名称。
  • a:显示所有的网络通信(默认不显示监听状态,选项a会显示)

2.2 pidof指令

pidof可以直接获取进程对应的pid,避免使用ps axj | grep ... 来查看进程pid。

语法:pidof [进程名称]

图2.1 使用ps axj | gerp 和 pidof两种方法获取sshd的pid

三. udp协议

3.1 udp的概念及特点

udp协议,全程面向数据报协议,是在网络通信中传输层用到的协议。udp协议具有以下的特点:

  • 无连接,在通信之前,客户端和服务端不需要预先建立连接即可通信。
  • 不可靠,在传输中可能出现丢包、报文乱序等问题。
  • 面向数据报,不能够灵活的控制发送和读取数据的次数,数据见具有明确的边界。
  • 编码简单,由于不需要可靠性控制,udp的实现成本要远低于tcp。

3.2 udp协议端格式

图3.1 udp报头格式

udp协议的报头固定8字节长度,其中16位目的端口号用于目的主机找的接受数据的进程,16位源端口号用于目的主机向源主机发回数据时确认端口号。

udp协议是面向数据报的,每次发送和接受的数据大小固定,而面向数据报就是由udp报头中的16位窗口大小支持的

16位窗口大小是整个udp报文(数据端)的大小,有效数据的大小 = 16位窗口大小 - 8字节

如果16位校验喝出错,报文就会直接被丢弃。

udp的报头,是通过结构体(位段)来定义的,使用位段是由于网络资源相对紧张,使用位段能够有效减少报头的长度,节约网络资源,在tcp协议中报头也是使用位段来组织的。下面为一段udp报头定义的demo代码。

struct udp_header
{
	uint32_t src_port : 16;
	uint32_t dst_port : 16;
	uint32_t udp_size : 16;
	uint32_t udp_check : 16;
};

 对于任何协议的报文,都需要结局两个关键问题:

  • 如何封装数据(添加报头)。
  • 如何向上交付数据(解包)。

对于udp协议的报文,我们看到,其报头的大小是固定的。因此,通过一下的操作,可以提取出报文中的有效数据并向上交付:

  1. 提取出8字节的报头。
  2. 提取报头中的16位窗口大小。
  3. 有效数据大小 = 16位窗口大小 - 8字节报头。
  4. 通过指针偏移,获取指向有效数据起始位置的指针。

3.3 对于面向数据报及应用层发送与读取数据的理解

对于udp协议面向数据报的理解:udp协议报文的长度是可以确定的,而由于其报头长度又是8字节,这样就可以确定有效数据的大小。接受方通过获取有效数据的大小,就可以读取固定长度的数据,这样也就实现了面向数据报。在udp中,一次发生的数据,接收方不可以分为多次读取。

对于应用层发送数据和读取数据的理解:在udp/tcp协议中,要维护发生缓冲区和接受缓冲区,udp协议由于是面向数据报的,一次发送固定长度的数据,所以udp没有发送缓冲区,但是udp有接收缓冲区。而tcp协议则既有发送缓冲区也有接受缓冲区。

  • udp协议没有发送缓冲区,有接收缓冲区。
  • tcp协议既有发送缓冲区也有接受缓冲区。

应用层调用send/sendto/recv/recvfrom这一类网络IO接口发送和接受数据,本质上并不是将数据直接传到网络中,而是将数据拷贝到内核中协议维护的缓冲区里,或者从缓冲区中拷贝数据到应用层,至于数据什么时候发送的网络,则是由OS关心的,与上层用户和协议无关。

图3.3 网络IO函数工作的本质

四. tcp协议的概念及特点

tcp协议,全称传输控制协议,属于网络通信中的传输层协议,传输控制协议,顾名思义就是可以保证网络通信可靠性的协议。

tcp协议具有以下特点:

  • 需要连接:在两台主机正式通信之前,需要三次握手建立连接,结束通信时需要四次挥手断开连接。
  • 通信可靠:一般不会出现丢包、报文乱序等问题。
  • 面向字节流:读写不需要一一匹配,应用层只负责向下交付数据,至于具体怎么发送数据,则完全由tcp控制。

五. tcp报头

5.1 tcp协议端格式

图5.1为tcp协议端的一般格式,分为三部分组成:20字节定长报头 + 选项 + 数据。

图5.1 tcp协议端格式

5.2 tcp报头各部分的含义

如图5.1所示,选项之前的20字节数据为tcp协议的定长报头,其中每一部分的含义和功能为:

  • 16位源端口号和目的端口号:用于确定通信双方主机上,是哪两个进程在进行进程间通信。
  • 32位序号和确认序号:用于确保网络通信的可靠性,每当一条报文发出,就要携带序号,而tcp为了保证传输数据的可靠性,就要求每一条发出去的报文要收到与之对应的确认收到的响应,确认序号表示该序号之前的报文以及被接收端接收了。确认序号和序号一定要区分,不可以使用同一序号来确认应答,因为tcp是全双工的,一条响应信息也有可能携带要发给对方的数据,而这条信息也需要被对方确认收到。
  • 4位首部长度:以4字节为基本单位,表示20字节报头 + 选项的总长度,4字节的数据可表示的范围是0 ~ 15,20字节定长报头 + 选项长度 = 4位首部长度 * 4字节,我们可以将tcp报文中的前部20位定长数据 + 选项 统称为报头,那么tcp报头长度范围是 20bytes ~ 60bytes。
  • 6个标志位:
  1. URG:紧急状态标志位,配合16位紧急指针使用,表示该报文不排队直接被接收方接收。
  2. ACK:确认应答标志位,表示该报文是对某一条接收到的报文的响应,在tcp协议网络通信中,大部分的报文ACK标志位都是1。
  3. PSH:催促标志位,配合16位窗口大小使用,如果发现对端迟迟没有接收数据的能力(接收缓冲区空间不足),那么就设置PSH标志位为1,催促对方尽快向上交付数据。
  4. PST:复位报文段,表示需要重新建立连接。如果因为某种异常原因,客户端与服务端的连接断开了,但此时客户端并不知道连接已经断开,仍然正常向服务器发送数据,服务器在收到数据时发现源客户端没有链接,那么就会向客户端发送PST标志位为1的报文,告知客户端需要重新进行三次握手建立连接。
  5. SYN:请求建立连接标志位,将带有SYN标识的报文称为同步报文段
  6. FIN:连接关闭标志位。用于通知对端,连接要关闭了,携带FIN的报文称为结束报文段
  • 16位窗口大小:用于获取对端的接收能力,即:对端的接收缓冲区还有多少剩余空间可用。
  • 16为检验和:如果检验不成功,就认为数据存在问题,需要客户端重发。
  • 16位紧急指针:配合URG标准位使用,表示要被对端优先紧急接受的内容相对于报文中数据起始位置的偏移量,注意对端只能接受一个字节的紧急内容,无法给定对端需要紧急接受的数据的长度。紧急指针一般用于检查服务器的状态,假设某台主机向服务器发送的数据迟迟得不到应答,那么就需要发送一个带有URG标识为的数据,获取对端相应,以确定是因为网络故障还是服务器本身崩溃引发的问题。在一些大型互联网厂商,会有一台控制主机控制多台服务器,这台控制设备会定期向每台服务器发送带有URG标识为的报文,以检验每台服务器的工作状态是否正常。

六. 三次握手和四次挥手

6.1 三次握手和四次挥手的流程

图6.1位三次握手和四次挥手通信双方(主机A和主机B)向对方发送的报文标识位信息以及双方主机所处的状态。当三次握手/四次挥手全流程结束后,通信双方的连接才正式的建立/断开。

图6.1 三次握手与四次挥手的流程以及双方主机所处的状态和操作

6.2 三次握手解析

如图6.1所示,三次握手建立连接时,主机A和主机B进行的操作流程为:

  1. 第一次握手:主机A的进程通过connect申请与主机B建立连接(主机B处于LISTE状态),即:主机A向主机B发送带有SYN标志位的报文。
  2. 第二次握手:主机B应答主机A,发送带有ACK标志位的报文,主机A收的主机B的应答后,即处于ESTABLISH状态,确认建立连接。
  3. 第三次握手:主机A建立好连接后向主机B发生ACK报文,主机B收到报文后随机处于ESTABLISH状态,完成建立连接的操作。

SYN -> SYN + ACK -> ACK的过程称为三次握手。

问题:为什么不能是一次握手,两次握手或四次握手?-- 从安全性、可靠性两方面考虑。

三次握手能在一定程度上提高安全性: 

  • 连接是需要管理的,管理连接需要消耗资源,OS会通过struct结构体描述连接,通过特定的数据结构组织连接,这需要消耗内存资源和CPU资源。
  • 如果采用一次握手:此时客户端只要大量向服务器发送SYN请求,就可以在短时间内让服务器充斥大量的连接,从而造成服务器瘫痪,这种状态被称为SYN洪水。
  • 如果采用二次握手:问题与一次握手本质上相同,主机A只需要丢掉主机B发回的ACK,就可以只在主机B建立连接而主机A中没有连接,引发SYN洪水。
  • 如果采用四次握手:那么主机A在第一次收到主机B的ACK应答后并不会建立连接,而收到两次ACK应答才会建立连接,那么主机A丢掉第二次的ACK报文,即可引发SYN洪水。
  • 三次握手的主要目的不是为了解决安全性,假设一个黑客同时控制数万台电脑,通过提前设置好的指令让这数万台电脑在同一时刻向某服务器发送SYN连接请求,那么一样可以瘫痪服务器,SYN三次握手,知识可以避免计算机小白攻击服务器。因为服务器建立一个连接,客户端就要对应的建立一个连接,客户端与服务器需要承受同等级别的资源消耗,而服务器配置一般远高于单台计算机,这样计算机小白就无法通过单台计算机攻击服务器。

三次握手保证可靠性(验证全双工):

  • 全双工,即通信双方同时具有收数据和发数据的能力。
  • 主机A向主机B发生连接请求,主机B会给主机A一个ACK应答,这样就可以检验主机A具有收数据发数据的能力。
  • 主机B在向主机A发送ACK应答时,也会收到来自主机A的ACK应答,这样就验证了主机B具有收数据和发数据的能力。

6.3 四次挥手解析

如图6.1所示,通信双方需要通过四次挥手断开连接,四次挥手过程中,主机A和主机B所执行的操作流程为:

  1. 第一次挥手:主机A调用close(fd)主动关闭连接,向主机B发送带有FIN标志位的报文。
  2. 第二次挥手:主机B收到主机A的FIN报文后,向主机A发送ACK应答,确认其收到了主机A的FIN请求。
  3. 第三次挥手:主机B在发送完ACK后,调用close(connfd)关闭连接,向主机A发送FIN报文。
  4. 第四次挥手:主机A收到主机B的FIN报文后,向主机B发送ACK,主机B再次收到ACK后结束四次挥手,断开连接。

在四次挥手中,有两个状态值的关注,第一个是TMIE_WAIT,另一个是CLOSE_WAIT。

TIME_WAIT状态的理解:

  • 当主动断开连接的主机(主机A)收到对端连接的FIN报文后,并不会立即将连接断开,而是会等待一定的时间才将连接状态设置为CLOSE,当连接处于CLOSE状态时,才算是真正的断开连接。
  • 在TIME_WAIT状态下,虽然连接理论上处于断开状态,但是其ip和端口号依旧处于占用状态,不能重新建立连接。
  • 如果一个服务器在关闭后处于TIME_WAIT状态,那么他就不能被立马重启,而是需要等过一段时间后,连接状态变为CLOSE才能够重启。 
  • TIME_WAIT状态存在的目的是为了清空网络中残存的数据,网络中残存数据,可能是由于丢包等问题产生的,如果下次建立连接后网络中中的残余数据还没有被清理掉,那么下次连接的SYN请求可能会失败,还可能对通信造成其他的一些干扰。
  • 一般来说,TIME_WAIT时间是2MSL,即:报文单向传输的最大时间,这样就可以基本保证通信双方在TIME_WAIT阶段能够进行一次数据的发送和应答操作,最大程度的保证网络中的数据被清空,但是无论如何也不可能确保网络中的数据100%清空,2MSL的等待时间可以极大程度确保清空网络数据。

某些时候,如果服务器挂掉不能立刻重启,会造成很大的损失,因此需要能够在TIME_WAIT状态下重启,通过setsockopt函数可以实现上面的功能。

int opt = 1;

setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

CLOSE_WAIT状态的理解:

如果在网络通信中,某连接长期处于CLOSE_WAIT状态,或者有大量连接处于CLOSE_WAIT状态,那么则是服务器程序写出了BUG。

造成大量CLOSE_WAIT状态的原因:服务器没有close(connfd)。这种情况下,服务器就不会向客户端发送FIN报文,也就无法将状态变换为LAST_ACK状态,造成服务器中存在大量的连接,消耗CPU和内存资源,最终导致服务器崩溃。

七. tcp协议保证可靠性和提高效率的机制

7.1 确认应答机制

在tcp协议中,主机每接收到一条报文,就要向对端发送一个ACK应答,告知对方收到了报文。

如图5.1所示,在tcp报头中,存在32位序号和32位确认序号,每一条带有ACK标志位的报文,其携带的确认序号的意义在于:告知对端该序号之前的报文已经收到了,并且告知对端下一次发送数据的起始序号

确认序号的两个作用:

  • 告知对端确认序号之前的报文全部被接收了。
  • 告知对端下次发送报文的起始序号。

在网络状况良好的情况,几乎不存在丢包问题,确认序号 = 序号 + 1。

如图7.1所示,我们可以将发送缓冲区看做是一个char类型的数组,其中每个待发送的char类型数据都有一个对应的下标位置,tcp协议单次发送数据的下标范围记为 [left, right],其中我们可以将发送的最后一个字符对应的下标right记为序号,right+1即是确认序号。

在tcp协议通信的过程也如图7.1所示,我们可以暂时将其视为是串行的过程,假设第一次发送报文的确认序号为1000,表示发生了下标为1~1000的数据,那么就会收到确认序号1001,下次从下标1001的位置开始发送数据,以此类推就是确认应答机制的工作原理。

图7.1 确认应答机制的原理与序号和确认序号的含义

7.2 超时重传机制

如图7.2所示,假设主机A发送给主机B的数据发生了丢包,或者主机B发送给主机A的确认应答报文发生了丢包,就会导致主机A迟迟收不到来自主机B的应答,那么主机A就会判定刚发出去的数据可能丢包,会触发超时重传机制,再次发送没有收到确认应答的报文。

图7.2 触发超时重传机制的两种情况

7.3 去重和按序到达机制

  • 去重机制:在tcp协议中,如果主机B发送给主机A的确认应答报文ACK发生了丢包,那么即使是主机B收到了对应的数据,主机A依然会按照数据丢包处理,进而触发超时重传机制。在这种情况下,主机B会收到两份同样的报文,为了保证tcp通信的可靠性,主机B就需要根据报文的序列号来进行去重,以避免上层应用收到两份相同的报文。
  • 按需到达机制:tcp协议可靠性的一点体现就是主机B接收报文的顺序与主机A发送报文的数据保持一致,即按序到达。但是,实际的网络环境十分复杂,会存在先发送的报文后到达的情况,此时作为接收端主机B,就需要根据报文中的序号来对报文进行排序,以保证报文的按序到达。

7.4 流量控制机制

  • 流量控制:通过获取对端接收能力,动态调整发送数据的速度。

接收端接受数据的能力是有限的,这是因为接受缓冲区的剩余大小是有限的。换句话说,就是主机从网络中接受数据的能力取决于接受缓冲区的剩余空间大小

如果接受缓冲区的剩余空间过小,而又有大量的数据从网络中发送到主机,那么就会造成接受缓冲区溢出的问题,溢出的这部分数据就会被丢弃,引发丢包造成通信过程不可靠。

但是,在保证可靠性的前提下,还应当注重网络通信效率的提升。我们希望达到这样的目的:对端接收能力强发送数据就快,接受能力弱发送数据就慢。为此,数据发送方就必须要获取对端接受缓冲区剩余空间的大小。

在tcp协议的报头中,有16位窗口大小,用于表示对端接收数据的能力(对端接收缓冲区大小),数据发送方根据窗口大小动态调整发送数据的速度。如果检测到16位窗口大小为0,则表示对端接收缓冲区没有剩余空间,会暂时停止数据的发送。

当由于检测到窗口大小为0而停止发送数据后,数据发送方会每隔一定的时间间隔向对端发送探测信息,以确定对端接收缓冲区是否有了剩余空间可以接收数据,如果检测到了窗口大小更新,那么数据发送就会恢复。

图7.3为流量控制机制的工作原理图。

提问:第一次发送数据时还没有收到应答,怎样确认对端的接收能力?在三次握手建立连接时,通信双方向对端发送的报文中,会携带窗口大小。

图7.3 流量控制的原理

7.5 滑动窗口机制

如果tcp协议按照图7.1中表示的串行方式发送数据,那么一个数据在发出后,只有收到对端的应答,才可以发送下一条数据,这样tcp协议相对于udp协议,效率就会十分低下。

为提高效率,tcp引入滑动窗口,即:在滑动窗口内的数据,不需要获取对端的应答即可发送

滑动窗口的大小,由对端接收能力和网络状况共同决定,一般我们认为:滑动窗口大小 = min(16位接收窗口大小, 网络拥塞窗口大小)。

滑动窗口,是位于发送缓冲区的滑动窗口,图7.6位滑动窗口的物理结构示意图,在存在滑动窗口的情况下,可以将发送缓冲区分为三块来看待:

  • 在滑动窗口左侧的数据,是已经被发出去并且收到确认应答的数据。
  • 在滑动窗口内部,是不需要收到对端应答就能发送的数据。
  • 在滑动窗口右侧,是暂时还不能发送的数据。
图7.4 滑动窗口的物理结构

当数据发送出去且收到应答,并且对端窗口的大小不变或者变大时,滑动窗口的右边界会向右移动,数据收到对端的确认应答后左边界也会向右移动。滑动窗口内的数据,是不断动态变化的。

如图7.5所示,主机A检测到主机B接受能力为4000,那么滑动窗口最初的范围就是[1,4000],其中的数据在没有收到确应答的时候就可以发出。假设主机A收到了确认序号为1001的报文,表示1~1000的数据被对端获取到了,且对端的接收能力变为4000不变,那么滑动窗口的左右边界都会向后移动,变为[1001,5000]。

图7.5 滑动窗口机制的原理

提问:滑动窗口向后移动,是否会出现越界问题?

答案是不会的。因为虽然发送缓冲区的物理结构是线性数组,但是其逻辑结构实际上为环形数组,假设滑动窗口的右边界为indexRight,那么其在实际的线性数组中对应的下标是 indexRight % N,其中N为发送缓冲区的最大容量。

图7.6 滑动窗口的物理结构和逻辑结构

这种情况下,如果主机B的某个确认应答发生了丢包,如图7.7所示,假设主机B的2001和3001确认应答在传输过程中丢包,但是4001号确认应答被主机收到了,这并不会影响后序数据的发送,因为确认序号的含义为:告知对端在确认序号之前的所有报文都被成功接收了

因此,只要主机A收到4001确认应答,就会自动认为4001之前的所有数据都被对端接收了,哪怕没有收到之前的确认应答。

图7.7 滑动窗口下的确认应答丢包问题

7.6 快重传机制

  • 快重传:如果主机三次收到确认应答序号相同的报文,那么就触发重传机制。 

还是以滑动窗口的场景为例,在图7.5中,假设数据1001~2000发生了丢包,那么即使主机B收到了3001~4000、4001~5000等报文,只要1001~2000还没有收到,主机B就只能给主机A发送1001确认应答,在这种情况下,主机A就会收到连续3个1001号确认应答,此时就会判定1001~2000可能发生了丢包,该数据会自动重传。

而主机B在收到重传后的1001~2000数据后,就会直接将确认序号更新到接收的序号最大的数据。

快重传机制与超时重传机制是相互配合的,如果一个报文丢失,但对端在一定时间内没有向源主机发送三个一样的确认应答,那么就必须由超时重传机制触发重传数据。

图7.7 快重传机制

7.7 网络拥塞控制机制

虽然tcp协议有滑动窗口以及流量控制机制可以提高数据传输的效率,但是,这只是在通信双方的主机层面来考虑的,在实际的网络数据传输中,还要考虑网络状况的好坏来动态调整发送数据的速度。为此,tcp协议引入了网络拥塞控制机制来根据网络状态调整数据发送频率。

图7.8为网络拥塞控制及调整的策略,遵循慢启动机制,即:在通信开始的时候,先发送少量的报文探测网络情况,如果能够顺利接受到确认应答,再不断加大数据大发送量,最多不能超过对端的接收能力。

慢启动机制,只是在发送数据的初期启动缓慢,其增长速度遵循指数级别增长,因此发送数据几次后,就会快速增长。

慢启动机制的控制策略可以总结为:

  • 通信开始阶段,先发送单位数据探测网络情况,此时记网络窗口大小为1。
  • 每一批发送的报文,如果全部收到了应答,那么网络窗口的大小就翻倍,我们可以理解为每收到一个ACK,网络窗口大小就+1。
  • 将网络窗口的大小与对端接受能力进行对比,取较小的值为滑动窗口大小。
图7.8 根据网络状况动态调整数据发送量的慢启动机制

由此,我们可以认为,慢启动机制在在初期遵循指数级别的增长,而指数级别增长由于后期会增长过快,导致网络中可能一瞬间塞满大量数据引发网络拥堵,为此引入一个阈值,当网络窗口大小达到这个阈值之后就不再符合指数增长,而是遵循线性增长策略。当网络窗口大小增长到发生网络拥塞时,就会重新开始慢启动机制,并将新的阈值更新为拥塞窗口大小的1/2。

总结网络窗口动态调整的策略为:

  • 前期慢增长阶段,遵循指数级别的增长。
  • 当网络窗口的大小达到某一阈值后,就变为线性增长,直到发送网络拥塞。
  • 发生网络拥塞就将窗口大小变回1,重新开始慢启动。
  • 将阈值更新为发生网络拥塞时窗口大小的1/2。
图7.9 网络窗口大小增长的策略

7.8 延时应答机制

  • 延时应答:收到报文不立马应答,而是等一段时间后应答。

假设主机A和主机B在进行通讯,主机B收到主机A发送过来的消息后,如果马上给主机A确认应答,那么新发过来的数据不会被上层应用取走,这样就会造成主机B接收缓冲区的剩余空间变少,将变少的剩余空间填入ACK报文的16位窗口大小处,就会降低主机A发送数据的速度。

如果在主机B收到报文后,不立即做出应答,而是等一段时间,让上层应用取走一部分数据,此时接收缓冲区就有了更多的剩余空间,这是后做出应答,对端就不会认为接收能力减弱了。

从接收报文个数N和接受后的间隔时间T来控制延时应答:

  • 当接收方连续接受N个报文后,就进行确认应答。
  • 收到报文后,经过时间T后应答。

N和T在不同的操作系统中有所不同,一般取N=2,T=200ms,图7.10为延时应答策略的实现原理。

图7.10 延时应答策略的实现原理

7.9 捎带应答机制

  • 捎带应答:在确认应答的同时携带其它数据/标志位,以减少报文发送次数的策略。

如图7.11所示,tcp协议通信在执行四次挥手断开连接时,主机B应答主机A断开连接请求的ACK报文和其自身的FIN请求可以合并为一个报文发送给对端,从而在事实上执行“三次挥手”。

图7.11 捎带应答机制 -- 事实上的“三次挥手”断开连接

八. 对listen接口的第二个参数的理解

listen函数 -- 设置监听状态,等待对端连接

函数原型:int listen(int sockfd, int backlog)

函数参数:

  • socked:socket文件描述符。
  • backlog:连接队列的长度。

返回值:成功返回对应的监听文件描述符,失败返回-1。

我们知道,在tcp通信时,服务器在接收客户端的连接请求是,是通过accept函数来实现的,但是accept本质上不参与三次握手建立连接的过程

accept的功能:将连接从内核层拿到应用层进行通信。

如果不调用accept,那么连接依旧可以被建立,只是没有拿到应用层中。而listen的第二个参数,我们可以称之为全连接队列的最大长度。如果服务器收到了大量的请求,但是应用层来不及调用accept来取走连接,那么就会在内核层暂时将这些连接使用队列的形式维护起来,等待有连接关闭的时候,就可以立即accept取走内核中连接队列中维护的连接。

backlog用于指定全连接队列的最大长度,这个参数不能太大也不能太小。

用生活中的场景来理解这个参数,假设一家很火爆的餐馆在高峰期店内桌子全部被占用了,这是再有顾客希望来就餐时,店家就会在店门口准备若干桌椅,将这些客户暂时“保存维护”起来,如果店内有客人离开,就立马可以有排队的客户进店用餐,这样能够最大程度上保证店内资源的利用率。

如果“等待队列”维护的太短或者干脆不维护,那么就有可能存在店内客户离开,空桌在一段时间内没有被利用起来,造成经济损失。如果维护的太长,那么队列后面的连接就需要等待很长时间才能够被上层拿走,不符合实际需求,况且可以将“等待队列”的一部分资源用于扩充店内桌椅。

综上,backlog不能太小是为了保证资源在任何时候都能够被充分利用,不能太大是为了避免全连接队列尾部的连接等待较长时间才能够被accept取走。

在OS内核中,会维护两种关于连接的队列:

  • 全连接队列:已经完成三次握手建立好的,但还没有被上层accept取走的连接。
  • 半连接队列:完成了三次握手的一部分,但还没有全部完成的连接。

全连接队列的长度由listen的第二个参数决定,如果全连接队列的长度达到了最大值,那么就无法让更多的连接进入到ESTABLISHED状态,进而处于半连接状态。处于半连接状态的连接有一定的生命周期,如果在一定时间内不能完成三次握手进入ESTABLISHED状态,那么半连接就会被销毁。

九. 对于tcp一些概念的理解

9.1 面向字节流

建立起一个基于tcp协议的通信,会在操作系统内核中创建一个发送缓冲区和一个接收缓冲区,对于发送缓冲区/接受缓冲区及面向字节流的理解如下:

  • 应用层调用网络IO接口,本质上并不是将数据写到网络中去,而是将数据拷贝到内核中的发送缓冲区或者从内核中的接收缓冲区拷贝数据到应用层。
  • 如果一次发送的数据太长,那么就可能分多次拷贝到发送缓冲区,同理如果接收缓冲区内的数据过多,则有可能分多次拷贝到应用层。
  • 在面向字节流通讯中,发送数据和接受数据不是一一对应的,而在udp协议(面向数据报)通信中,发送数据和接受数据必须一一对应。

面向字节流,就好比通过自来水管取水,我们一次拿到的水,可能是供水商分多次供给的,也可能是供水商一次供水量的一部分。面向数据报,就好比取快递,我们那N个快递,那发送方就一定发了N个快递,不能看发快递的和取快递的次数不匹配。

在tcp协议中,应用层只负责交付数据,至于怎么发送、什么时候发送,则完全由tcp协议控制,应用层完全不需要关系,用于层也无需关系数据的格式,只管向下交付数据即可。

9.2 粘包问题

什么是粘包问题:

  • 由于tcp协议是面向字节流的,因此每个报文数据之间是没有明确边界的。
  • 这样就可以导致在某一时刻,发送缓冲区中同时存在多组数据。
  • 应用层从内核接收缓冲区读取数据时,可能将多个数据包混在一起,这就是粘包问题。

由此可见,粘包问题主要是由于数据包之间边界不明确引起的,那么要解决粘包问题,就必须要明确不同数据包之间的边界,解决粘包问题是应用层的责任:

  • 如果数据包是定长的,那么每次只需要读取定长数据即可。
  • 可通过特定的标识符来明确数据包之间的边界。
  • 可以在数据的起始位置显示的给出数据包的长度。

9.3 tcp异常情况

  • 进程终止:向对端发送FIN请求,四次挥手断开连接,与正常的断开连接没有区别。
  • 计算机重启:重启关机前会发生FIN,与进程终止相同。
  • 计算机断电或网络故障:接收端认为连接还在, 一旦接收端有写入操作接收端发现连接已经不在了,发送报文请求连接重置使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在如果对方不在也会把连接释放

9.4 udp实现可靠通信的方法

仿照tcp实现可靠通信的方法即可,只不过是把实现可靠通信的手段从内核层移动至应用层。

可以考虑的方法有:

  • 在应用层引入序号和确认序号。
  • 引入确认应答,确保不丢包。
  • 引入超时重传。
  • 按照应用层序列化对报文进行排序去重。
  • ... ...

十. 总结

  • 网络通信时基于五元组实现的:源IP、目的IP、源端口号、目的端口号、协议号。根据端口号的编号值,可将端口号划分为知名端口号和操作系统动态分配端口号。
  • netstat指令可用于查看网络状态,pidof指令可用于根据进程名获取进程pid。
  • udp协议全程用户数据报协议,具有无连接、不可靠、编码简单的特定;而tcp协议全程传输控制协议,具有有连接、传输可靠、编码复杂的特定。
  • tcp协议建立连接需要三次握手,断开连接需要四次挥手,在建立和断开连接的过程中,通信双方的主机状态是不断在变化的,主动断开连接的一方处于TIME_WAIT状态时无法重新建立连接,而如果存在大量连接处于CLOSE_WAIT状态则说明服务端代码没有close(connfd)。通过setsockopt可以解决TIME_WAIT状态无法重新连接的问题。
  • tcp通过引入:超时重传机制、确认应答机制、去重机制、按序到达机制等来保证数据传输的可靠性;通过引入:流量控制机制、快重传机制、网络拥塞控制机制、延时应答机制、捎带应答机制等来提高通信效率。其中流量控制和快速应答机制既有保证可靠通信的功能,也有提高效率的功能。
  • listen的第二个参数表示底层全连接队列的最大长度,这个参数既不能太大也不能太小。
  • tcp协议是面向字节流的,数据发送和接收不需要一一对应,这样就可能引发粘包问题,解决粘包问题的关键在于明确区分数据包之间的边界。tcp协议的异常情况可以分为大致分为:进程终止、计算机重启、计算机断点、网络故障等,这几种状态连接都会断开。
  • udp协议可以在应用层引入保证tcp保证可靠性的机制,从而达到使用udp协议一定程度实现可靠通信的目的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1081061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎么把视频转换成mp4格式?

怎么把视频转换成mp4格式?如果你经常在网上下载视频的话,那么应该知道视频文件的格式种类非常的多,不同网站下载到的视频格式各不相同,除了最常见的mp4格式外,其它视频格式基本上都存在着兼容问题,如果格式…

Qt creator下载安装

版本问题: Qt4的开发环境包括3个基本部分:Qt Framework(Qt库)、QtCreator(IDE)和MinGW(编译调试),都要分别下载安装并配置,比较麻烦。 Qt5之后,…

Vuex->vuex获取值以及改变值,vuex异步请求

vuex获取值以及改变值vuex异步请求 1.vuex获取值以及改变值 Vuex分成五个部分: 1.State:单一状态树 2.Getters:状态获取 3.Mutations:触发同步事件 4.Actions:提交mutation,可以包含异步操作 5.Module&…

2023云栖大会即将开幕,速来免费预约!

2023云栖大会 500热点话题 1000行业实干家 现场实操 即学即用 40000平米科技展 3000前沿展品 72小时沉浸式科技体验 与数字人李白共吟盛唐诗歌 视频云边缘云云通信 深入3大Tech领域,覆盖20话题演讲 虚拟演播室、智能全景声 AIGC与数字人的一站式制作 “AI更…

重温mybatis之一篇带你入门Mybatis

一.前言 我是一个五年得java开发工程师,在印象里,从第一年我就会使用mybatis了,从最开始得xml配置,到spring整合,再到springboot。无非就是写个mapper抽象类,写个mapper.xml方法。就这么一直用着。期间也想…

ASP.Core3.1 WebAPI 发布到IIS

本篇文章简述如何在IIS中发布你的.Core 程序 1.打包 首先你要打包好你程序,如果你是Visual Studio开发的程序,那你右击你的项目点击发布 如果你是Visual Code 开发的,那你在你的终端切换到你的目录然后执行命令 dotnet publish --config…

mysql面试题43:MySQL自增主键用完了怎么办?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:MySQL自增主键用完了怎么办? 当MySQL中的自增主键用完了(…

Python数组删除元素pop与remove对比

pop()和remove()函数都可以用来删除列表中的函数,pop()是按索引来删除的,remove()是按元素来删除的。 1、pop()默认删除列表中最后一个元素,而且会返回删除的元素。此时的时间复杂度为O(1) 下面的例子中,…

pdf怎么合并在一起?

pdf怎么合并在一起?对于pdf合并这个问题,有的小伙伴想很简单,只需要将文件直接复制再其中的一个后面不就完事了吗。其实不然,因为我们如果要是需要将很多文件进行合并的话,就会产生很多问题的。总之,在现在…

搭建一个自己的学术语音助手(1)

背景: 大模型出来后语音助手借着LLM的语义理解、知识组织能力的提升,升级了一波buffer。然后在使用这些语音助手的时候总觉得缺了点什么,但也讲不出来具体缺了什么。这几天的思考突然有了灵感,其实缺的就是自己的知识内容如何变成…

NFT合约分析:ERC721A

概述 读者可前往我的博客获得更好的阅读体验。 本文主要介绍标准NFT实现的一个变体,即ERC721A合约实现的相关细节。ERC721A是由著名NFT系列Azuki提出,该系列NFT是著名的蓝筹NFT。本文主要聚焦于Azuki提出的ERC721A合约的代码细节分析。 与传统的ERC72…

APP订单管理软件生活中不可或缺|小程序系统定制开发|网站搭建

APP订单管理软件生活中不可或缺|小程序系统定制开发|网站搭建 它不仅可以帮助人们更好地管理自己的生活,还可以提高工作效率,让生活更加便捷。那么,APP订单管理软件在实际应用中的优势有哪些呢?qq:258715719 v&#…

Nginx下载安装与配置(linux)

一、Nginx下载 官网下载地址:nginx: download 推荐下载稳定版: 二、Nginx依赖环境 执行安装命令,安装完成后可查看版本进行验证。 1.GCC C 用于编译和构建C语言程序的开源编译器。 #安装GCC C软件包 yum install -y gcc-c #查看GCC C版…

TCP/IP(四)TCP的连接管理(一)三次握手

一 tcp连接回顾 部分内容来自小林coding TCP篇 记录的目的: 亲身参与进来,加深记忆 ① 引入 前面我们知道: TCP 是面向连接 [点对点的单播]的、可靠的、基于字节流的传输层通信协议面向连接意味着:在使用TCP之前,通信双方必须先建立一…

手动抄表和自动抄表优缺点对比

随着科技的发展,自动抄表技术已经越来越成熟,被广泛应用于各个领域。然而,手动抄表在一些特定场景下仍然具有一定的优势。本文将从手动抄表和自动抄表的优缺点入手,对比分析它们的应用场景和使用价值。 1.成本低:手动抄…

判断一个类是否为另一类的子类issubclass()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 判断一个类是否为另一类的子类 issubclass() 选择题 下列代码执行输出的结果是? class A: pass class B(A): pass class C: pass print("【执行】print(issubclass(A,A))") print(i…

spring boot+ vue+ mysql开发的一套厘米级高精度定位系统源码

UWB室内高精度定位系统源码,自主版权演示 UWB技术最核心的能力就是精准的定位与测距,当然它还具备通信功能。不过,目前主流通信技术已经相当成熟,无需UWB兼顾去做通信传输。而且,如果使用UWB通信功能,反而会…

中性配色能给室内空间带来怎样的魅力

虽然人们对中性卧室总是有一些议论,甚至有些人强烈反对中性卧室,认为它们缺乏特色或个性,但你不得不承认,当有人把中性卧室设计得恰到好处时,视觉上会非常愉悦。 室内设计师设计中性卧室的技巧 我们认为中性卧室设计中…

计算机毕业设计选题推荐-springboot 小说阅读平台

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

Java 反射机制到底是什么?

Java 反射机制是 Java 语言的一个重要特性。大家应该先了解两个概念,编译期和运行期。 编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放…