今天在实现TCP/IP协议栈中的连接管理管理功能的时候,遇到一个TIME_WAIT状态,处于TIME_WAIT状态的tcp socket 在等待2个Max Segment Lifttime以后就会转移到closed状态。
觉得有些奇怪,以下是我的一点理解。
首先这个TIME_WAIT一定是从FIN_WAIT_2转移过来的。进入TIME_WAIT意味着这个tcp socket在主动关闭的时候,自己的FIN已经被对方确认,而且对方也发了一个FIN过来表示对方也没有什么数据要主动传输了。
因此,处于TIME_WAIT等待这么久的时间首先有第一个作用: 对 对方FIN的ACK确认有可能在传输过程被丢弃。于是对方会重新发送FIN,停留在TIME_WAIT可以对重传的FIN进行确认。否则,对方将一直处于LAST_ACK状态。
第二个作用就比较隐蔽了,TCP的TIME_WAIT故意让主动关闭连接的socket等待这么长的时间是为了让这个socket的那些旧的仍然在网络中传输的数据包能够从整个网络清空出去。
既然socket都跑到TIME_WAIT为什么还有在空中飞的旧数据包呢?这主要是一些超时的但是又没有被丢弃的数据包,这些数据包在前面的传输中被检测到超时了,于是tcp启动了超时重传机制,而且还走了新的、更不拥塞的链路,于是导致这些重传的数据包反而更快被对端确认。
这些姗姗来迟的旧数据包如果在TIME_WAIT阶段到达,就会被丢弃掉。
然而,如果这个主动的关闭的socket 没有等待足够长的时间,而是又用着相同的端口号去建立一个新的TCP连接,那么这些旧的数据包就干扰新的连接。虽然,新的连接可以通过seq和ack来识别出这些旧数据包的seq、ack不合理,但是TCP的规范是要求不能这么搞的,因为TCP规范并没有要求所有的初始序列号怎么选择,如果刚好选择的初始序列号和旧的数据包的seq吻合了,那么就很麻烦了。
也正是因为有TIME_WAIT状态的存在,导致我们在主动关闭一个socket的时候,尽管包含这个socket的进程已经关闭了,但是这个socket还处与TIME_WAIT状态,再次启动这个进程是无法再去bind这个socket直到这个socket从TIME_WAIT转移到CLOSED去。