首先查看TCP建立连接与断开连接的两对端的状态图,如下:
由图可知客户端在收到服务端的请求断开连接带有的FIN位结束报文段,并发送ACK确认以后,其并没有直接关闭,进入到CLOSED状态,而是转变为TIME_WAIT状态。
- TIME_WAIT状态是做什么的?
在这个状态,顾名思义,即客户端连接要等待一段长为2MSL(报文段最大生存时间)的时间,才能关闭连接,在标准文档RFC 1122的MSL建议值为2min. 为什么存在此状态?
(1)可靠的终止TCP的连接:即如上图,最后客户端确认服务器结束报文段6的ACK报文段7若传输中丢失,服务器没收到确认报文段,则会重发结束报文段,因此客户端需要停留在此状态以处理重复收到的结束报文段,从而向服务器发送确认报文段。若客户端此时已经变为closed状态,它则会以复位报文段来回复服务器,服务器则会认为这是一个错误。
(2)保证让迟来的报文段有足够的时间被识别丢弃:当一个TCP连接处在TIME_WAIT状态,它则会依然占用当前的端口,新的连接时无法立即使用的,但当没有这个状态,当立即有新的TCP连接时,其会使用与刚才相同的IP地址与端口号,此时这个新的连接则极有可能收到原来连接的迟到的TCP报文段,这明显是不应该发生的,所以设置这个状态是极有必要的。为什么TIME_WAIT状态要等待2MSL时间?
由于TCP报文段最大生存时间为MSL,其保持2MSL时间可以确保网络上两个传输方向的尚未接收到的、迟到的报文段都已经消失,或被路由器丢弃,而2MSL时间后建立新的连接其绝不会收到原来连接的应用程序数据。
但在一些情况下,如:一般一个服务器的IP与端口号是固定的,但若这个服务器主动关闭连接异常终止,我们此时则需要立即重启此服务器,但由于它此时在TIME_WAIT状态则先会占着此IP地址与端口号,导致它会重启失败,而我们要改变这种情况,可用以下函数解决:
int setsockopt(int sockfd,int level,int optname,const void* optval,socklen_t optlen);
//可如以下如此调用:
int opt=1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
以上SO_REUSEADDR参数可强制进程立即使用处于TIME_WAIT状态连接占用的端口。