一、TCP断开连接的过程
下面以客户端主动发起断开请求为例:
1.第一次挥手
客户端发送FIN报文,表明客户端将不在发送数据。
具体过程:把FIN标志位改为1,序号seq=u(之前发送的数据加1,这里规定即使不携带数据序号也要+1。
该过程中客户端通过close从ESTABLISHED状态进入FIN_WAIT_1。
2.第二次挥手
服务端收到客户端发来的连接释放报文(FIN),发送确认报文。
具体过程为:ACK 标志位置为1,确认序号=u+1,自己的序号v。该过程中服务端进入CLOSE_WAIT状态,TCP服务器通知高层应用进程客户端已经没有数据要服务送了,但是服务端可能还有数据要发送,所以这段时间服务端仍然可以正常向客户端发送数据。
客户端收到服务端的确认报文后进入FIN_WAIT_2状态,等待服务端发送连接释放报文(FIN),在收到FIN之前仍需要接受服务端发送的数据。
3.第三次挥手
服务端发送完最后的数据之后,就向客户端发送连接释放报文(FIN)。
具体过程为:FIN标志位置为1,确认序号= u+1,序号为w(可能之前的半关闭状态又发送了数据)。
服务端此时进入LAST_ACK状态,等待着客户端最后的确认。
4.第四次挥手
客户端收到服务端发送的FIN,发送最后的确认并且等待2MSL。
具体过程为:ACK标志位置为1,确认序号=w+1,自己序号=u+1。
服务端收到客户端的ACK之后立即进入CLOSED状态,但是服务端发送完ACK之后进入TIME_WAIT状态,等待2MSL之后进入CLOSED状态,所以服务端总是先于客户端关闭。
二、注意事项
1.为什么客户端发送最后的ACK之后需要等待2MSL?
(1)保证客户端最后发送的ACK可以到达服务端。
思考这样一个场景,如果不等待2MSL客户端发送最后一个ACK之后即CLOSED状态,但是最后ACK丢失,所以服务端永远不会收到客户端发送的ACK就一直处于LAST_ACK状态。
但是等待2MSL就可以解决这个问题,如果最后的ACK丢失,但是客户端不关闭而是处于TIME_WAIT状态,假设最坏的情况下最后的ACK在网络中存活了MSL状态恰好到达服务端"“门口"的时候丢失,客户端没有收到ACK重发FIN,历时MSL到达客户端"门口”,客户端收到FIN知道ACK丢失重发ACK后重置时间再次等待2MSL,所以通过设置TIME_WAIT状态总能保证服务端收到最后的ACK。
(2)防止已经失效的报文影响下次连接。
再来思考这样一个场景,服务端发送FIN后由于网络原因(路由循环等)客户端暂时没有收到,服务端等待一段时间后认为丢失,重新发送FIN,客户端收到第二次的FIN回执ACK后,二者断开连接(没有等待2MSL)。然后二者在次在相同的IP、端口上通过再次正常建立连接,此时网络中的FIN报文到达客户端,客户端认为服务端发送了主动关闭请求,从而影响了 本次连接。
但是等待2MSL就不一样了,在这2MSL内客户端处于ITIME_WAIT状态,网络中的FIN最多存活MSL,所以TIME_WAIT状态内FIN必定会丢弃,从而保证在建立新连接的时候,上次连接所有的报文都丢弃。
2.客户端建立连接后突然故障怎么办?
TCP有一个保活计时器,顾名思义就是保证对端活着的计时器,每次收到对端报文后都会重置该计时器,计时器时间是两小时,如果两小时内没有收到对端的消息就会向对端发送探测报文段,以后每隔75分钟发送一个探测报文段,连续10次没有收到应答就认为对端故障,然后断开连接。