主动方和被动方
TCP关闭连接不区分客户端和服务端,哪一端都可以主动发起关闭连接请求。所以为了描述方便,描述中的“主动方”表示主动发起关闭连接一方,“被动方”表示被动关闭连接一方。
主动端状态
主动端可能出现的状态:FIN_WAIT1、FIN_WAIT2、CLOSING、TIME_WAIT。
FIN_WAIT1
主动方调用close函数关闭连接后立刻进入FIN_WAIT1状态,此时只要收到对端确认ACK之后马上就会进入FIN_WAIT2状态。因为对端确认ACK是TCP协议栈自己控制的,所以很快就会发出。出现场景:主动方等待ACK过程中网络断掉了,导致长时间收不到ACK,主动方就回停留在CLOSE_WAIT1状态上(超时时间:一般默认60s超时)。此时我们可以使用netstat -anpt命令看到这种状态。
这个状态如果超时,将直接进入CLOSED状态。超时时间通过tcp_fin_timeout参数配置。
FIN_WAIT2
主动端在等待对端FIN到来的过程中,会一直保持这个状态(超时时间:一般默认是60s)。由于网络中断,或者对端很忙还没来得及发送FIN,或者对端有bug忘记关闭连接等都会导致主动端长时间处于FIN_WAIT2状态。如果主动方发现大量的FIN_WAIT2状态时,应该引起相关人员的注意,这可能是网络不稳、对端程序bug的表现。
这个状态如果超时,将直接进入CLOSED状态。超时时间通过tcp_fin_timeout参数配置。
TIME_WAIT
主动方收到对端的FIN后进入TIME_WAIT状态。然后发送最后一个确认ACK到对端。之后等待2个最大的报文存活周期(这是因为:一是保证残留网络报文不会被新连接接收而产生数据错乱,由于自己上一次发送的数据报文可能还残留在网络中,等待2MSL时间可以保证所有残留的网络报文在自己关闭前都已经超时。二是确保自己最后ACK发送到对端,因为ACK发送也可能会发送失败,这时对端会重新发送FIN,如果已经CLOSED了那么对端将收到RST而不是ACK了,这不符合TCP可靠关闭的策略。),正常的关闭流程客户端TCP连接都会经过这个状态,最终进入CLOSED状态。所以我们使用netstat -anpt命令发现客户端有很多的TIME_WAIT,一般这都是正常的现象。
这个状态的连接还没有真正关闭,所以占用的文件句柄和端口号等资源也没有释放。如果TIME_WAIT状态的连接过多,将会导致文件句柄或者端口号等资源不足。如果你想控制这个状态连接的数量,你可能需要详细了解:tcp_max_tw_buckets、tcp_tw_recycle、tcp_rw_reuse三个系统配置。
对于有大量短连接服务的服务器来说,服务端主动关闭连接会产生大量的TIME_WAIT,如果不及时回收这些个连接会造成资源严重浪费。不过一个应用层系统都有自己最大并发连接数限制、操作系统也有最大TIME_WAIT连接限制数,所以一般也不会产生大的问题。
CLOSING
双方几乎同时调用了close接口主动关闭连接,此时都进入了FIN_WAIT1状态。如果在FIN_WAIT1状态期望收到对方的ACK但收到了对方的FIN,这时候双方都进入了CLOSING状态。然后都给对方一个ACK确认,收到了ACK后都会进入CLOSED状态了。
这个状态如果超时,将直接进入CLOSED状态。
被动端状态
被动端可能出现的状态:CLOSE_WAIT LAST_ACK。
CLOSE_WAIT
这个状态表明TCP连接等待被关闭。只可能在被动方出现。如果被动方存在大量的CLOSE_WAIT状态需要因为我们的特别注意了。我们要仔细研究确认为什么被动方迟迟不愿关闭连接(或许是我们程序中的bug开启了连接,用完后却忘记关闭)。
目前开发过程中遇到如下这个场景导致被动方有很多的CLOSE_WAIT状态:A是一个应用程序,B是一个tomcat服务器。A开了一个连接connection,发送请求给B。A接收相应数据后没有调用connection.close关闭连接,在A端垃圾回收这些connection对象前,这些连接一直保持着B端的连接超时后会主动发起关闭连接请求给A,此时A进入了CLOSE_WAIT状态,B进入了FIN_WAIT2状态,由于A迟迟不发送FIN给B,B端触发timeout直接进入了CLOSED状态。
这样一个场景B端由于有超时设置一个为60s,不会存在大量的FIN_WAIT2状态。
但是A端就会残留大量的CLOSE_WAIT状态(CLOSE_WAIT状态也有超时,但是太大,默认为43200s,详情见tcp_timeout_close_wait系统配置)。还好A端的java虚拟机的最大对内存配置较小,由于CLOSE_WAIT状态连接同样占用了内存资源,数量很多后就会触发垃圾回收,此时A端的CLOSE_WAIT的连接connection对象就会被销毁了(同时内存和句柄、端口等资源也被释放了)。
很明显这是一个bug导致的问题,如果没有及时回收的话,就会把内存、句柄或者端口等资源给用完,导致程序crash掉。
LAST_ACK
这个状态只可能在被动端出现。当被动端调用close接口关闭连接后便会进入这个状态,同时发送一个FIN给对端。在接受对端的ACK确认后便会进入CLOSED状态,这个状态一般不易出现,除非网络中断,一般对端会很快给与响应的。
总结
主动端出现大量的FIN_WAIT1时需要注意网络是否畅通、出现大量的FIN_WAIT2需要仔细检查程序为何迟迟收不到对端的FIN(可能是主动方或者被动方的bug)、出现大量的TIME_WAIT需要注意系统的并发量/socket句柄资源/内存使用/端口号资源等。
被动端出现大量的 CLOSE_WAIT 需要仔细检查为何自己迟迟不愿调用close关闭连接(可能是bug,socket打开用完没有关闭)。
参考:http://backend.blog.163.com/blog/static/202294126201411524415197/
本文详细解析了TCP连接关闭过程中的各种状态,包括主动端的FIN_WAIT1、FIN_WAIT2、CLOSING、TIME_WAIT状态,以及被动端的CLOSE_WAIT、LAST_ACK状态,并探讨了这些状态下的常见问题和解决方案。


1635

被折叠的 条评论
为什么被折叠?



