在理解了TCP连接建立于断开的过程后,再来看TCP的状态转换图就相对容易了。
图中有两个典型的状态转换路径,第一个是客户端申请建立连接与断开连接的过程,如图中黑色粗线所示:与前面描述的一致,在客户端通过发送一个SYN
包,主动向服务器申请一个连接,数据包发出后客户端进入SYN_SENT
状态等待服务器的ACK
和SYN
包返回,当收到这个返回包后,客户端对服务器的SYN
进行确认,然后自身进入ESTABLISHED
状态,与前面描述的三次握手过程完全一致。
当客户端申请断开连接时,它要发送FIN
包给服务器申请断开连接,当FIN
包发送后,客户端进入FIN_WAIT_1
状态等待服务器返回确认包,当收到这个确认包后,表明客户端到服务器方向的连接断开成功,此时客户端进入FIN_WAIT_2
状态等待服务器到客户端方向的连接断开,此时当客户端收到服务器的FIN
包时,即向服务器返回一个ACK
包,表明服务器到客户端方向的连接断开成功,此后客户端进入TIME_WAIT
状态,在该状态下等待2 MSL
后,客户端进入初始的CLOSED
状态。在连接处于2 MSL
等待时,任何迟到的数据报文段将被丢弃。此过程与断开连接的四次握手过程完全相符。
另一个典型的状态转换路径是描述服务器的,如图中虚线所示。服务器建立连接一般属于被动过程,它首先打开某个端口,进入LISTEN
状态以侦听客户端的连接请求。当服务器收到客户端的SYN
连接请求,则进入SYN_REV
状态,并向客户端返回一个ACK
及自身的SYN
包,此后,服务器等待客户端返回一个确认包,收到该ACK
包后,服务器进入ESTABLISHED
状态,并可以和服务器进行稳定的数据交换过程。可见,连接建立的过程和前面描述的三次握手过程完全一致。
当服务器收到客户端发送的一个断开数据包FIN
时,则进入CLOSE_WAIT
状态,并向上层应用程序通告这个消息,同时向客户端返回一个ACK
包,此时客户端到服务器方向的连接断开成功;此后,当服务器上层应用处理完毕相关信息后会向客户端发送一个FIN
包,并进入LASK_ACK
状态,等待客户端返回一个ACK
包,当收到返回的ACK
包后,此时服务器到客户端方向的连接断开成功,服务器端至此进入初始的CLOSED
状态。此过程与断开连接的四次握手过程完全相符。
上面这个转换图中还有两处较特殊的转换路线,它们分别用于处理TCP两端同时发起或断开连接的情况。
两台主机同时执行打开操作的握手包交互如下图所示,两台主机在同时发送自身的SYN
请求包后各自进入SYN_SENT
状态,等待对方的ACK
包返回,但一定时间后,每个主机都收到对方的SYN
包而不是ACK
包,此时两端都判定已经遇到了同时打开的状况发生,两端都进入SYN_RCVD
状态,并对对方的SYN
进行确认并再次发送自己的SYN
包,当接收到对方的ACK+SYN
后,两边都进入ESTABLISHED
状态。从这一点看,状态转换图中的从SYN_RCVD
到ESTABLISHED
转换的条件应该有两个,而图中只标出了一个。一个同时打开的连接需要交换4
个报文段,比正常的三次握手多一个。要注意的是在这样的连接过程中,没有将任何一端称为客户或服务器,因为每一端既是客户又是服务器。
两台主机同时发起主动关闭操作的握手包交互如下,当两个主机的用户程序同时执行关闭操作时,两主机都向对方发送一个FIN
包,并进入FIN_WAIT_1
状态等待对方的ACK
返回,但一段时间后,双方各自都收到对方的FIN
包,而不是ACK
包,此时两主机都判定遇到了双方同时主动关闭的状况,此时,两个主机就没有必要进入FIN_WAIT_2
状态等待对方的FIN
包了,因为这个包刚刚已经收到,而是直接进入CLOSING
状态,并向对方发送一个FIN
包的确认,等到双方都收到对方的ACK
包后,两边都各自进入TIME_WAIT
状态。
再来详细讲解下**TIME_WAIT
状态**,协议中是这样描述的:当 TCP执行一个主动关闭,并发出最后一个ACK
后,该连接必须在TIME_WAIT
状态停留的时间为2
倍的MSL
。这样可让TCP保证在最后的这个ACK
丢失的情况下重新发送ACK
(另一端超时并重发最后的FIN
)。处于TIME_WAIT
等待状态的TCP端口此刻还不能被其他新连接所使用。
虽然上面的状态转换图上指出,从一个连接从LISTEN
状态转换到SYN_SENT
状态是允许的,但是大多数的协议实现中均没有实现该转换,即执行被动打开的连接一般不要主动发起连接。
平静时间:如果主机在TCP状态转换过程中突然崩溃,在TCP重启后的一个MSL
内,TCP不能发送任何数据报文段,这段时间称为平静时间。设置平静时间是为了防止旧连接的延迟的数据报分组对新连接造成影响。