网络通信
一、TIME_WAIT状态
TCP
连接中,如果客户端主动发起关闭连接请求,当客户端收到服务器端的结束报文后,并没有直接进入CLOSED
状态,而是进入TIME_WAIT
状态。
在这个状态,客户端连接要等待2MSL
(Maximum Segment Life, 报文段最大生存时间)的时间,才能完全关闭,MSL
是TCP
报文段在网络中的最大生存时间,标准文档RFC 1122
的建议值是2min。
CentOS7 规定的TIME_WAIT
时间为60s
。
TIME_WAIT
状态存在的原因有两点:
- 可靠的终止
TCP
连接 - 保证让迟来的
TCP
报文段有足够的时间被识别并丢弃
对于第一个原因,假如四次挥手过程中,最后一次的确认应答报文丢失,,那么服务器将重发结束报文段。因此客户端将需要停留在某个状态以处理重复收到的结束报文段(再次向服务器发送确认报文段)。否则,客户端将以复位报文段来回应服务器,服务器则认为这是一个错误,因为他期望收到的是一个确认应答报文。
在Linux
系统中,一个TCP
端口不能被同时打开多次。当一个TCP
连接处于TIME_WAIT
状态时,我们将无法立即使用该连接占用着的端口来建立新的连接。因为TCP
报文段的最大生存时间为MSL
,所以坚持2MSL
时间的TIME_WAIT
状态能够确保网络上两个传输方向上尚未被接收到的、迟到的TCP
报文段都已经消失(被中转路由器丢弃)。因此,可以在2MSL
时间之后安全、正确的建立新连接,而绝不会接收到属于原来连接的应用程序数据。
二、解决TIME_WAIT状态引发问题的办法
对客户端来说,我们通常不担心重启问题,因为客户端一般使用系统自动分配的临时端口号来建立连接,而由于随机性,临时端口号一般和程序上一次使用的端口号(还处于TIME_WAIT
状态的的那个连接的端口号)不同,所以客户端一般可以立即重启。
但是对于服务器来说,如果是服务器主动关闭连接后异常终止,则因为他总是使用同一个知名服务端口号,所以连接的TIME_WAIT
状态将导致他不能立即重启。
解决办法:通过设置socket
选项SO_REUSEADDR
来强制进程立即使用处于TIME_WAIT
状态连接占用的端口。
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
通过这种强制端口复用的办法,可以使服务器立即重启。但是也必然会引发一些问题,但是没有办法,服务器发送复位报文,再次建立连接。