四次握手流程
先来一张TCP断开连接流程图:(图片来自于网络)
上图中,A是主动关闭方,B是被动关闭方,四次握手可以描述为:
- 第一次握手:A告诉B,“我要关闭连接了”。
- 第二次握手:B回复A,“我知道你要关闭了,但是请等一下,我还有数据没有传完,你等我消息”。
- 第三次握手:B告诉A,“我的数据发完了,你可以关闭连接了”。
- 第四次握手:A回复B,“好的,你先关吧,我2MSL时间后再关”。
出现大量CLOSE_WAIT的原因
被动关闭方收到主动关闭方发来的FIN,则会回应ACK,并进入CLOSE_WAIT状态。但如果被动关闭方不执行close(),就不能由CLOSE_WAIT迁移到LAST_ACK。在该状态下,recv/read会返回0。
举例来说,当主动关闭方调用closesocket的时候,被动关闭方的应用程序正在调用recv,这时候有可能应用程序没有收到主动关闭方发来的FIN包,而是由TCP代回了一个ACK,所以就陷入CLOSE_WAIT出不来了。
TCP的KeepLive功能,可以让操作系统替我们自动清理掉CLOSE_WAIT的连接。但是KeepLive在Windows操作系统下默认是7200秒,需要调整一下:
打开注册表(运行 -> 输入regedit -> 回车),在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters里修改:
KeepAliveTime REG_DWORD 300000
KeepAliveInterval REG_DWORD 1000
TcpMaxDataRetransmissions REG_DWORD 5
为什么要有TIME_WAIT
让4次握手关闭流程更加可靠。
若最后一个ACK丢失,被动关闭方会重新发一个FIN。在TIME_WAIT状态内,主动关闭方收到重发的FIN后,会重发ACK。而倘若没有2MSL的TIME_WAIT状态,当主动关闭方收到重发的FIN后,会回复一个RST,被动关闭方在收到RST后,会将其解释成一个错误:connect reset by peer。