一、TCP连接断开的四波:
由于 TCP 连接是全双工的,因此每个方向都必须单独关闭。
其原理是当一方完成其数据传输任务时,可以发送一个FIN来终止该方向的连接。收到一个FIN只表示这个方向没有数据流,一个TCP连接收到一个FIN后仍然可以发送数据。首先关闭的一侧将执行主动关闭,而另一侧执行被动关闭。
(1) TCP 客户端发送一个 FIN 以关闭客户端到服务器的数据传输(段 1).
(2)服务器收到这个FIN,发回一个ACK,确认序号是收到的序号加1(段2)。和SYN一样,一个FIN会占用一个序号。
(3) 服务器关闭客户端的连接并向客户端发送一个 FIN (段 3).
(4)客户端段发回ACK报文确认,并将确认序号设置为收到的序号加1(段4).
双方状态详情:
ESTABLISHED:双方已建立连接。
FIN_WAIT_1:这个状态需要好好解释。其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义是等待对方的FIN报文。这两种状态的区别在于:FIN_WAIT_1状态实际上是当SOCKET处于ESTABLISHED状态时,它要主动关闭连接,向对方发送一个FIN报文。此时SOCKET进入FIN_WAIT_1状态。当对方响应ACK报文时,进入FIN_WAIT_2状态。当然,正常情况下,不管是什么情况,对方都应该立即响应ACK报文。因此,FIN_WAIT_1 状态一般比较难看到。有时可以通过 netstat 看到 FIN_WAIT_2 状态。
FIN_WAIT_2:这个状态上面已经详细解释过了。事实上,处于FIN_WAIT_2状态的SOCKET代表了一个半连接,即一方请求关闭连接,同时也告诉对方我暂时还有一些数据要发送给你。关闭连接。
TIME_WAIT:表示收到对方的FIN报文,发送ACK报文。2MSL 后可以返回到 CLOSED 可用状态。如果处于FIN_WAIT_1状态,当收到对方发来的同时带有FIN标志和ACK标志的消息时,可以不经过FIN_WAIT_2状态直接进入TIME_WAIT状态。
CLOSE_WAIT:这个状态的意思其实就是在等待关闭。怎么理解?当对方关闭一个 SOCKET 并向自己发送一个 FIN 消息时,你的系统无疑会向对方回应一个 ACK 消息,然后进入 CLOSE_WAIT 状态。接下来,你真正需要考虑的是,看看你是否还有数据要发送给对方。如果没有,那么可以关闭SOCKET,向对方发送FIN报文,即关闭连接。所以你处于CLOSE_WAIT状态,需要做的就是等待你关闭连接。
LAST_ACK:这个状态比较容易理解。发送FIN报文后等待对方ACK报文的被动关机方。当收到 ACK 消息后,可以进入 CLOSED 可用状态。
我们可以看到主动关闭方在收到对方发送的FIN报文后会进入TIME_WAIT状态,等待2MSL。
二、TIME_WAIT原因:1.实现TCP全双工连接的可靠释放
TIME_WAIT 是等待足够的时间,以保证主动关闭方发送的最后一个 ACK 可以被被动关闭方接受,从而使其正常关闭。从TCP状态转换图中可以看出,假设发起主动关闭的一方(客户端)发送的最后一个ACK在网络中丢失了。由于 TCP 协议的重传机制,执行被动关闭的一方(服务器)将重新发送其 FIN。在FIN到达客户端之前,客户端必须保持连接状态,这意味着TCP连接对应的资源(客户端的local_ip,local_port)不能立即释放或重新分配,直到对方重传的FIN到达. 客户端重新发送ACK后,TCP 连接只有经过 2MSL 时间段后才能恢复初始的 CLOSED 状态,而没有收到对方的 FIN。如果主动关闭方不保持这样的TIME_WAIT状态,那么当被动关闭方重传的FIN到达时,主动关闭方的TCP传输层会回复对方一个RST包,由对方为错误。不过,这个其实上面只是关闭连接的正常过程,并非异常。主动关闭方的 TCP 传输层会以一个 RST 包响应对方,对方会认为这是错误的。不过,这个其实上面只是关闭连接的正常过程,并非异常。主动关闭方的 TCP 传输层会以一个 RST 包响应对方,对方会认为这是错误的。不过,这个其实上面只是关闭连接的正常过程,并非异常。
2.为了使旧的数据包因过期而在网络上消失
TIME_WAIT状态会持续2MSL,这个时间足够长,两个方向的数据包都会被丢弃,导致原本连接的数据包在网络中自然消失。再次出现的数据包一定是新连接产生的。
MSL(Maximum Segment Lifetime)最大数据包寿命
每个 TCP 实现都必须选择一个 MSL。这是丢弃任何段之前网络内的最长时间。这个时间是有限的,因为 TCP 段作为 IP 数据报在网络内传输,它的 TTL 时间限制了它们的生命周期。RFC 793 规定 MSL 为 2 分钟,实际上通常使用 30 秒或 1 分钟。
为了说明这个问题,我们首先假设TCP协议中没有TIME_WAIT状态限制,然后假设当前有一个TCP连接:(local_ip, local_port, remote_ip, remote_port)。四元组建立新的连接。TCP 连接由四元组唯一标识。因此本地连接收不到数据包,在我们假设的情况下,TCP 协议栈无法区分前后两个 TCP 连接的区别。在它看来,这简直就是同一个连接,中间先释放再建立的过程,对他来说并不是“感知”的。这样,可能会发生上一个TCP连接的一方发送的数据到达另一方后,会被本方的 TCP 传输层接收为当前 TCP 连接的正常数据,并向上传递给应用层(实际上,在我们假设的场景中,在旧数据到达之前,旧连接断开,并且建立了相同的四元组组成的新TCP连接,所以这些旧数据不应该向上传递到应用层),结果数据混乱,导致各种不可预知的奇怪现象。TCP作为可靠的传输协议,必须在协议层面考虑和避免这种情况,这是TIME_WAIT状态存在的第二个原因。旧连接断开,建立一个由同一个四元组组成的新TCP连接,所以这些旧数据不应该向上传递到应用层),结果数据混乱,导致各种不可预知的怪现象。TCP作为可靠的传输协议,必须在协议层面考虑和避免这种情况,这是TIME_WAIT状态存在的第二个原因。旧连接断开本地连接收不到数据包,建立一个由同一个四元组组成的新TCP连接,所以这些旧数据不应该向上传递到应用层),结果数据混乱,导致各种不可预知的怪现象。TCP作为可靠的传输协议,必须在协议层面考虑和避免这种情况,这是TIME_WAIT状态存在的第二个原因。
TIME_WAIT过多的危害
过多的 TIME-WAIT 状态有两个主要危害:
二次危害会造成严重后果。你要知道,端口资源也是有限的。一般可以开放的端口是32768到61000,也可以通过以下参数设置来指定:
net.ipv4.ip_local_port_range
如果“connector initiating”的TIME_WAIT状态过多,占用所有端口资源,则无法创建新的连接。
客户端(发起连接的一方)受限于端口资源:
服务器(被动连接方)受系统资源限制:
参考链接:
4.1 TCP 三向握手和四向挥手面试题
TCP面试常见问题:原因、危害以及如何避免time_wait状态