4次挥手流程
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
- CLOSE_WAIT
CLOSE_WAIT是被动关闭连接是形成的,根据TCP状态机,服务器端收到客户端发送的FIN,TCP协议栈会自动发送ACK,连接进入CLOSE_WAIT状态。但如果服务器端不执行SOCKET的CLOSE()操作,状态就不能由CLOSE_WAIT迁移到LAST_ACK,则系统中会存在很多CLOSE_WAIT状态的连接.
- 半关闭(
half-close
)
TCP中,连接的一方可以停止发送数据,但仍然可以接收数据,这称为半关闭。
- 2MSL
MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。
假设:主动方A, 被动方B
-
第一次挥手: A发送FIN(连接释放请求,FIN报文段不携带数据,它只消耗一个序号)给B;A不再发送数据给B了,但是B还能收到数据,B当然也能发送数据
-
第二次挥手: B收到A发过来的FIN后,发送一个ACK(应答响应)给A,确认序号为收到序号+1;此时B进入CLOSE_WAIT状态。(这是个被动过程)
-
第三次挥手: B发送一个FIN给A,用来关闭B到A的数据传送, B便进入LAST-ACK状态。此时B不再给A发送数据了
-
第四次挥手: A收到B过来的FIN后,在向B发送ACK确认应答。A处于
TIME_WAIT
状态,等待2MSL
后就close了
-
正常TCP
-
ACK接收失败重新FIN,ACK(2MSL意义)
服务端大量close_wait
在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT
状态。通常持续时间应该很短,用netstat等命令查看可能看不到。
如果出现长时间且大量的close_wait
状态,那么很大可能是出现什么问题了;可能原因:
服务端接口耗时突然较长,客户端请求读超时而主动断开了连接,而服务端又忙于读/写操作,没有关闭这些半连接状态的socket, 那么就会导致服务端就会出现大量close_wait
。
危害: 占用内存,占用系统连接数/打开文件数,线程数超过tomcat的默认最大配置等问题
模拟大量close_wait
- 定义controller
@RestController
public class TestControl {
@GetMapping(value = "/test/cost")
public String getTest() throws Exception {
long cur = System.currentTimeMillis();
try {
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
}
long cost = System.currentTimeMillis() - cur;
return "hello test:" + cost;
}
}
- jmeter模拟qsp=10,且设置了连接/读超时时间的请求
- 启动并观察结果
客户端大量读超时
服务端大量CLOSE_WAIT