TCP Close_Wait问题

[size=large][b]生成原因[/b][/size]:
如果我们的Client程序处于CLOSE_WAIT状态的话,说明Socket是被动关闭的.
因为如果是Server端主动断掉当前连接的话,那么双方关闭这个TCP连接共需要四个packet:
Server ---> FIN ---> Client
Server <--- ACK <--- Client
这时候Server端处于FIN_WAIT_2状态,也就是传说中的半连接状态;而我们的程序处于CLOSE_WAIT状态。
Server <--- FIN <--- Client
这时Client发送FIN给Server,Client就置为LAST_ACK状态。
Server ---> ACK ---> Client
Server回应了ACK,那么Client的Socket才会真正置为CLOSED状态

既然如此,我们的程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明还没有发FIN给Server,那么可能是在关闭连接之前还有许多数据要发送或者其他事要做,导致没有发FIN

[size=large][b]原因分析[/b][/size]:
1. 难道Client根本不知道自己收到了FIN包
当Server调用closesocket的时候,Client正在read中,这时候有可能对方发送的FIN包我没有收到,而是由TCP代回了一个ACK包,所以我这边套接字进入CLOSE_WAIT状态.但是我在read的时候,会判断返回值是否已出错,是错误值的话就主动closesocket,这样防止没有接收到FIN包。

2. 为什么大量连接都处于CLOSE_WAIT,难道服务器端总是主动断开连接?
有一种可能就是没有重用本地地址和端口,一个端口不行,总是换另外一个来用,导致大量的端口进入CLOSE_WAIT状态。也许我们无法避免被冻结在CLOSE_WAIT状态永远不出现,但起码可以保证不会占用新的端口。加上类似的代码保证重用
sc.setReuseAddress(true);


3. 网上查到强行关闭的问题
socket.setSoLinger(boolean on,int linger)

设置SO_LINGER为0,CloseSocket的时候,不论是否有排队数据未发送或未被确认。这种关闭方式称为“强行关闭”,因为Socket的虚电路立即被reset,尚未发出的所有数据都会丢失。在远端的read都会抛出IOException.

4.最终的问题
根据分析得到,只要Client端主动调用了closeSocket,一定会发送FIN包,并改变自己的状态。因此debug程序代码,发现问题的真正所在。因为系统关闭socket是异步调用,在关闭事件执行之前,有异常抛出,导致异步EventHandlerThread死掉,然后事件被丢弃。想办法fix了这个问题,让
sc.close();
被真正执行了,一切OK.

[size=large][b]总结[/b][/size]:
当发起主动关闭的左边这方发送一个FIN过去后,右边被动关闭的这方要回应一个ACK,这个ACK是TCP回应的,而不是应用程序发送的,此时,被动关闭的一方就处于CLOSE_WAIT状态了。如果此时被动关闭的这一方不再继续调用closesocket,那么他就不会发送接下来的FIN,导致自己老是处于CLOSE_WAIT。这个时候如果去做read或者write,就会抛出IOException,必须成功调用closesocket,才会发送一个FIN给主动关闭的这一方,同时也使得自己的状态变迁为LAST_ACK。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章