最初客户端和服务器都处于ESTABLISHED
状态,假设客户端先发起关闭请求:
1.第一次挥手
客户端发送一个FIN报文,报文指定一个序列号seq=u,此时客户端处于
FIN_WAIT1
状态,即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认
2.第二次挥手
服务器收到
FIN报文
,发送一个ACK报文(ack=u+1),表明已经接收到客户端报文了;此时服务器处于CLOSE_WAIT
状态;即服务器收到释放连接的信号后发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),并进入CLOSE_WAIT
(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务器的连接释放。客户端收到服务器的确认后,进入FIN_WAIT2
(终止等待2),等待服务器发出连接释放报文段
3.第三次挥手
如果服务器也想关闭连接,则发送一个
FIN
报文,并且指定一个序列号seq=w,此时服务器处于LAST_ACK
状态;即服务器发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务器端进入最后确认状态,等待客户端的确认
4.第四次挥手
客户端接收到服务器发送的FIN报文,同样会发送一个ACK报文作为响应(ack=w+1),此时客户端处于
TIME_WAIT
状态;之后确定服务器端收到自己的ACK报文后进入CLOSED状态,服务器端收到ACK报文之后,就关闭连接,处于CLOSED
状态;即客户端接受到服务器的连接释放报文后,发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT
(时间等待)状态。但此时TCO连接并未完全关闭,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态