通常情况下,一个正常的TCP连接,都会有三个阶段:
- TCP三次握手;
- 数据传送;
- TCP四次挥手;
具体流程可以看下面的状态图。
一、分析CLOSE_WAIT现象和问题定位Spring Boot应用内置Tomcat服务出现大量CLOSE_WAIT的现象,主要原因是某种情况下对方关闭了Socket链接,但是我方(Server)忙于读或者写,没有关闭连接。
关于CLOSE_WAIT的产生大部分都是资源没释放导致的,有httpclient导致的,也有数据库或者其他中间件服务器(如Redis、MQ等等)连接导致的。
CLOSE_WAIT状态是由于客户端关闭了socket连接,发送了FIN报文,服务端也发送了ACK报文,此时客户端处于FIN_WAIT_2状态,服务端处于CLOSE_WAIT状态。
下面回顾一下问题的定位:
1. 首先,我这边的大部分请求都需要查询数据库,数据库连接池设置的最大连接数是100,所以每一个请求创建了一个连接,等到100个请求就把连接池占满了,但是处理servlet的那个线程并没有释放这个连接,于是接下来的请求再去创建数据库连接的时候就会一直阻塞在那里,这里我所用的是DBCP作为连接池的。
2. 如果没有可用的连接对象会导致线程等待。好了,servlet由于得不到数据库连接而阻塞了,这个客户端的请求就一直等待。
3. 客户端使用httpclient设置了5s的请求超时时间,那么超时之后就会抛出异常,关闭连接。
4. 关闭连接时,客户端发送了FIN(终结)报文,Server端TCP/IP返回了ACK报文,但是由于处理请求的线程还处于阻塞的状态,所以当前的连接状态是CLOSE_WAIT。
二、终止TCP连接的四次挥手由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。
假设终止FIN命令由Client端发起。
当Client端传输完成数据,或者需要断开连接时:
1. Client端发送一个FIN报文给Server端。(序号为m)
- 表示要终止Client到Server这个方向的连接。
- 通过调用close(socket) API。
- 表示Client不再会发送数据到Server端。(但Server还能继续发给Client端)。
- Client状态变为FIN_WAIT_1。
2. Server端收到FIN后,发送一个ACK报文给Client端。(序号为m 1)
- Server状态变为CLOSE_WAIT。
- Client收到序号为(m 1)的ACK后状态变为FIN_WAIT_2。
3. Server端也发送一个FIN报文给Client端。(序号为n)
- 表示Server也要终止到Client端这个方向的连接。
- 通过调用close(socket) API。
- Server端状态变为LAST_ACK。
4. Client端收到报文FIN后,也发送一个ACK报文给服务器。(序号n 1)
- Client状态变为TIME_WAIT。
5. Server端收到序号为(n 1)的ACK
- Server的状态变为CLOSED。
6. Client等待2MSL之后
- Client的状态也变为CLOSE.
至此,一个完整的TCP连接就关闭了。
两个基本问题:
Q: 我们看到CLOSE_WAIT出现在什么时候呢?
A: 在Sever端收到Client的FIN消息之后。
Q: 状态CLOSE_WAIT在什么时候转换成下一个状态呢?
A: 在Server端向Client发送FIN消息之后。
至此似乎明白了为什么会出现CLOSE_WAIT的状态:如果Server端一直没有向Client端发送FIN消息(调用close() API),那么这个CLOSE_WAIT会一直存在下去。
从上面我们看到出现CLOSE_WAIT,说明Server端没有发起close()操作,这基本上是用户Server端程序的问题了,比如存在耗时的操作、访问DB超时或无法获取连接等等。
通常情况下,Server都是等待Client访问,如果Client退出请求关闭连接,Server端自觉close()对应的连接。
三、SYN,ACK,RST和FIN都是什么的缩写?连接过程是通过一系列状态表示的,这些状态有:LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT和 CLOSED。
各个状态的意义如下:
LISTEN - 侦听来自远方TCP端口的连接请求;
SYN-SENT - 在发送连接请求后等待匹配的连接请求;
SYN-RECEIVED - 在收到和发送一个连接请求后等待对方对连接请求的确认(ACK);
ESTABLISHED - 代表一个打开的连接,数据可以传送给用户;
FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
FIN-WAIT-2 - 从远程TCP等待连接中断请求;
CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
CLOSING - 等待远程TCP对连接中断的确认;
LAST-ACK - 等待Client端TCP的连接中断请求的确认;
TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认;
CLOSED - 没有任何连接状态;
英文单词缩写:
SYN(Synchronize)同步;该标志仅在三次握手建立TCP连接时有效,表示一个新的TCP连接请求。
ACK(ACKnowledge)确认;对TCP请求的确认标志,同时表示对方系统已经成功接收所有数据。
RST(Reset)重置;
FIN(Finish)终结;用来结束一个TCP会话。
,