目录

什么是TCP四次挥手 ? 

TCP 四次挥手过程是怎样的?

为什么挥手需要四次?

第一次挥手丢失了,会发生什么?

第二次挥手丢失了,会发生什么?

接收到第二次挥手后的两种场景

第三次挥手丢失了,会发生什么?

第四次挥手丢失了,会发生什么?

为什么 TIME_WAIT 等待的时间是 2MSL?

为什么需要 TIME_WAIT 状态?

TIME_WAIT 过多有什么危害?

服务器出现大量 TIME_WAIT 状态的原因有哪些?

如何优化 TIME_WAIT?

服务器出现大量 CLOSE_WAIT 状态的原因有哪些?​


什么是TCP四次挥手 ? 

我们知道TCP协议是有连接的,可靠性传输,全双工,面向字节流的传输层协议, 使用TCP协议在客户端和发送端传输数据前,必须先建立连接,传输数据完成之后,就要断开连接.而对于四次挥手来说就是用来TCP断开连接的.

TCP 四次挥手过程是怎样的?

  • 首先客户端要关闭连接,先把报文中标志位FIN置为1,然后向服务端发送FIN报文表示要关闭连接.之后客户端进入FIN_WAIT1状态
  • 服务端收到客户端发来的FIN报文之后,内核会自动回复一个ACK给客户端,之后服务端进入CLOSED_WAIT状态.
  • 等待服务端进程调用close函数,也就是等待服务端处理完数据之后,服务端在给客户端发送FIN报文表示请求断开连接,之后服务端进入LAST_ACK状态
  • 客户端收到服务端发来的FIN报文之后,会回复最后一个ACK报文,客户端进入TIME_WAIT状态,2MSL后,进入closed状态  (注意只有主动关闭连接的一方才会有TIME_WAIT状态)
  • 服务端接收到客户端的最后一个ACK报文后,就会进入closed状态,关闭连接

为什么挥手需要四次?

首先根据四次挥手的过程可以知道每一方都要发送一个FIN报文和一个ACK报文

  • 客户端发送FIN报文表示请求断开连接,仅仅表示客户端不在发送数据了,但是还能够接收数据
  • 服务端收到客户端的FIN报文后,内核会自动的回复ACK报文,等待服务端进程调用close函数,也就是等待服务端进程处理完数据之后,服务端会向客户端发送FIN报文,表示要断开连接.

可以看出,服务端发送的ACK报文并不能和FIN报文结合在一起发送,因为要等待服务端进程处理完数据之后才可以关闭连接, 所以是四次挥手.

第一次挥手丢失了,会发生什么?

我们知道首先客户端会向服务端发送FIN报文表示客户端想要断开连接,之后客户端进入FIN_WAIT1状态,服务端接收到客户端的FIN报文之后内核立即会返回一个ACK报文.

如果第一次挥手丢失,也就是客户端发送给服务端的FIN报文丢失,那么当客户端没有收到服务端发送的ACK报文,就会触发超时重传机制,如果之后接收到了ACK,报文会继续进入四次挥手过程,如果没有 会继续重传,达到重传次数 客户端还没有收到ACK报文,那么会等待一段时间一般是上一次报文发送时间的2倍, 如果还没有收到,客户端会主动关闭连接

第二次挥手丢失了,会发生什么?

服务端收到客户端发送的FIN报文之后,会回复给客户端ACK报文,如果ACK报文丢失,客户端没有收到服务端的ACK报文,那么客户端就会超时重传FIN报文,如果之后接收到了ACK,报文会继续进入四次挥手过程,如果没有 会继续重传,达到重传次数 客户端还没有收到ACK报文,那么会等待一段时间一般是上一次报文发送时间的2倍, 如果还没有收到,客户端会主动关闭连接

接收到第二次挥手后的两种场景

当客户端接收到服务端返回的ACK报文之后,就会等待服务端发送FIN报文,这段时间客户端处于FIN_WAIT_2的状态.

如果是close函数关闭的连接,表示不能够发送和接收数据,所以处于FIN_WAIT_2状态有时长限制,一般为60s,如果超过这个时长客户端还没有收到服务端的FIN报文,那么客户端就会主动关闭连接.

如果是shutdown函数关闭的连接,表示只是不能发送数据,但是可以接收数据,如果一直没有收到服务端发送的FIN报文也就是第三次挥手,会一直死等下去.

第三次挥手丢失了,会发生什么?

当服务端进程处理完数据之后就会进行关闭连接,内核就会向客户端发送FIN报文表示请求关闭连接,之后服务端就会进入LAST_ACK状态,会等待客户端返回的ACK报文.

当服务端进程调用close函数的时候->数据处理完之后才可以关闭连接,内核没有权利替代服务端关闭连接,必须由服务端主动调用close函数来关闭连接

如果服务端一直没有收到客户端返回的ACK报文,那么就会触发超时重传,服务端会重传FIN报文,如果之后接收到了ACK,服务端进入closed状态,如果没有 会继续重传,达到重传次数 客户端还没有收到ACK报文,那么会等待一段时间一般是上一次报文发送时间的2倍, 如果还没有收到,服务端会主动关闭连接.

另外呢,如果是使用的close函数关闭的连接,如果超过时长tcp_fin_timeout限制一般为60s,不会一直处于FIN_WAIT_2状态,客户端会主动关闭连接.

第四次挥手丢失了,会发生什么?

当服务端处理完数据之后就会调用close函数来关闭连接,会给客户端发送FIN报文,服务端进入LAST_ACK状态,客户端收到之后会返回一个ACK报文,客户端进入Time_WAIT状态等待2MSL后进入close状态,服务端接收到最后一个ACK报文之后进入closed状态.

如果第四次挥手丢失,也就是客户端返回的ACK丢失,服务端就会触发超时重传机制,重传FIN报文,如果之后服务端接收到了ACK,服务端就会进入closed状态,如果没有, 会继续重传,达到重传次数服务端还没有收到客户端发来的ACK报文,那么会等待一段时间一般是上一次报文发送时间的2倍, 如果还没有收到,服务端会主动关闭连接

另外,客户端收到第三次挥手之后,客户端返回给服务端ACK报文,客户端就会进入TIME_WAIT状态,会等待2MSL,如果中途再次收到第三次挥手,会重置定时器, 当等待2MSL之后,客户端就会主动关闭连接进入closed状态.

为什么 TIME_WAIT 等待的时间是 2MSL?

首先说什么是MSL,TTL,以及两者的关系

MSL就是最大的报文生成时间,MSL是网络报文生存的最长时间,超过这个时间,报文将会被丢弃,因为TCP是基于IP协议的,TTL是经过路由器的最大跳数,每经过一个路由器,TTL就减一,当减到0的时候报文就会被丢弃,同时发送ICMP报文给源主机.

TTL 与 MSL的区别 : TTL是经过路由的最大跳数,MSL是报文生存的最长时间,要确保MSL>=TTL才能保证报文是正常消亡.

为什么要等待2MSL呢 ?

原因是 在网络中可能来自发送方发来的数据报,然后接收方要给对方一个响应. 这样报文一来一回的时间就是2MSL.

比如当最后一个ACK报文丢失(也就是第四次挥手丢失),服务器那边就会触发超时重传机制,重传FIN报文,当FIN报文到达客户端,客户端再回一个ACK响应,这样一来一回等待的时间是2MSL.

2MSL时长允许报文至少丢失一次,当ACK报文丢失的时候,重发的FIN会在第二个MSL到达客户端,这样TIME_WAIT状态就可以应对.2MSL是当第三次挥手到达客户端的时候就会开始计时,当中途FIN报文再次到达客户端,定时器就会被重置为2MSL.

为什么不是 4MSL或者8MSL呢 ?

由于丢包概率很小,加入丢包概率为1/100,那么第二次丢包就是1/10000, 所以我们可以忽略,性价比会更高.

为什么需要 TIME_WAIT 状态?

主要有两个原因

  • 防止旧的报文进入了相同的四元组连接中
  • 能够保证被动关闭方正常关闭.

防止旧的报文进入了相同的四元组连接中

假设没有Time_WAIT状态或者TIME_WAIT时间太短 , 会发生如上情况 . 首先因为报文发送了但是被网络延迟,所以触发超时重传机制,重传该报文,然后对方返回ACK报文,之后就进行四次挥手,又开启了一个四元组相同的连接(源ip,端口,目的ip,端口相同的连接),这时因网络延迟的报文刚好到达,也正好在接收方的接收窗口内,导致接收方错误的接收历史报文.

为什么会造成历史报文会被新的四元组连接接收呢 ?

原因是 与序列号有关.因为序列号会发生回绕现象并不是无限递增的

我们再来回顾一下 序列号 与 初始化序列号.

  • 序列号 : 序列号是一个TCP报头的字段,在使用TCP协议的时候,因为TCP是面向字节流的协议,所以双发会按照字节为单位发送数据,字节的编号就是序列号为了保证TCP协议的顺序性和可靠性,序列号可以保证发送的报文到达接收端不会乱序,丢失后重传,接收重复的数据会丢弃,收到数据后确认, 序列号每增加到4G就会发生回绕(也就是会再次从0开始)-->并不是无限递增的.
  • 初始化序列号 : 在TCP建立连接的时候(发送三次握手),就会为双方各自生成一个初始化序列号,要确保每一个连接都要有不同的初始化序列号,三次握手时也要同步双方的初始化序列号.初始化序列号基于时钟周期,可以认为是一个32位的计数器,每4微秒+1,循环一次需要4.55小时

如果有了TIME_WAIT状态,在TIME_WAIT状态这2MSL中就能够确保任意一方的报文都自然地消失在网络中,保证新的连接中不会出现历史报文,一定是新连接所产生的报文.

能够保证被动关闭方正常关闭.

假设如果没有TIME_WAIT状态或者TIME_WAIT状态时间过短,接收方收到对方的FIN报文,返回个ACK报文,立马就进入了closed状态,当返回的ACK报文丢失,发送方会重传FIN报文,但是接收方已经进入了closed状态,就会回一个RST报文,这样发送方结束到一个错误的终止,就会异常的关闭连接, 这样对于TCP可靠性传输协议并不是正常的终止方式.

 如果有了TIME_WAIT状态,并且不会过短,也就是2MSL,他就在这等待2MSL的期间,1MSL期间ACK丢失,第二个MSL会重传FIN报文刚好2MSL,接收到重传的FIN报文就会重置定时器为2MSL. 这2MSL是至少允许最后一个ACK丢失一次.

TIME_WAIT 过多有什么危害?

TIME_WAIT状态过多主要是由两种危害 :

  • 占用端口资源,端口是有限的
  • 占用系统资源,比如内存资源,文件描述符,cpu资源,线程资源.....

TIME_WAIT过多主要分为两种情况,一种是客户端TIME_WAIT过多,一种是服务端TIME_WAIT状态过多.

如果是客户端的TIME_WAIT状态过多 : 

我们知道只有主动断开连接的一方才会有TIME_WAIT状态,客户端的TIME_WAIT过多可能导致占用大量的端口,把端口占满了从而使得客户端无法再跟同一个IP+端口相同的服务端进行通信了,但是由于端口是可以重复使用的所以也可以跟不同的服务端进行连接,因为内核在定位一个连接的时候是通过TCP四元组(源ip,目的ip,源端口,目的端口)来定位的

如果是服务端的TIME_WAIT状态过多:

由于服务端是固定监听某一个端口的,所以不会导致占用过多的端口资源,但由于服务端一直主动断开连接,开启过多的TCP连接,导致占用大量的内存资源,文件描述符,CPU资源,线程资源...

服务器出现大量 TIME_WAIT 状态的原因有哪些?


如何优化 TIME_WAIT?

服务器出现大量 CLOSE_WAIT 状态的原因有哪些?

参考小林coding计算机网络部分: 小林coding

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐