TCP连接管理和状态转换
前言
前面提到建立一个TCP连接需要3个报文段。而关闭一个TCP连接需要4个报文段。TCP协议还支持连接处于半开启状态,但这种情况并不常见。存在上述半开启状态的原因在于TCP的通信模型是双向的。这也意味着在两个方向中可能会出现只有一个方向正在进行数据传输的情况。
所以,本文主要介绍除了正常连接建立和终止之外的几种特殊连接,即半关闭、半开启、同时打开和同时关闭。
本文内容
1 TCP半关闭
TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所谓的半关闭。
如下图所示的半关闭,首先发送的两个报文段与TCP正常关闭完全相同:客户端发送的FIN,接着是服务器端返回对FIN的ACK。由于接收到半关闭的一方仍能够发送数据,因此在之后,服务器端可以向客户端发送任意数量的报文段(图中只画出了一个报文段)。当接收半关闭的一方完成了数据发送后,它将会发送一个FIN来关闭本方的连接,同时向客户端发出一个文件尾指示。当第2个FIN被确认后,整个连接完全关闭。
2 TCP半开启
如果在未告知另一端的情况下通信的一端关闭或终止连接,那么就认为该条TCP连接处于半开状态。这种情况发生在通信一方的主机崩溃的情况下。只要不尝试通过半开连接传输数据,正常工作的一端将不会检测出另一端已经崩溃。
产生半开连接的另一个共同原因是某一台主机的电源被切断而不是正常关机。
例如,某些个人电脑运行了远程登录客户端,并且在一天结束时关闭。如果在电源被切断时没有数据在传输,那么服务器就永远不会知道该客户端已经消失,服务器可能还一直以为该连接处于ESTABLISHED状态。当第二天用户启动电脑并开始新的一个会话时,服务器会启动一个新的服务进程。这样就会导致服务器上有很多半开的TCP连接。当然,TCP有自己的方法来应对这种情况,具体见下文。
3 同时开启 && 同时关闭
虽然两个应用程序同时主动打开连接看似不大可能,但是在特定安排的情况下是有可能实现。通信双方在接收到来自对方的SYN之前必须先发送一个SYN;两个SYN必须经过网络送达对方。该场景还要求通信双方都拥有一个IP地址和端口号,并且将其告知对方。上述十分少见,一旦发生,可称为同时打开。
例如,主机A的一个应用程序通过本地的7777端口向主机B的8888端口发送一个主动打开请求,与此同时主机B的应用进程也通过本地的8888端口向主机A的7777端口提出一个主动打开请求,此时就会发生一个同时打开的情况。上述的情况不同于主机A的一个客户端连接主机B的一个服务器,而同时又有主机B的一个客户端连接主机A的一个服务器的情况。在这种情况下,服务器(A或B主机的服务器)始终是连接的被动打开者而非主动打开者。而各自的客户端也会使用不同的端口号。
下图表示了一个同时打开过程中的报文段交换情况。
一个同时打开过程需要交换4个报文段,比普通的三次握手增加了1个。由于通信双方都扮演了客户端和服务器的角色,因此不能都将任何一方称为客户端或服务器。
普通关闭连接时,通信一方(通常是客户端,但不一定总是)提出主动关闭请求,并发送首个FIN。而在同时关闭中,通信双方都会完成上述工作,即通信双方都会发送FIN报文段。下图表示了同时关闭过程中报文段交换的过。
同时关闭需要交换与正常关闭相同数量的报文段。两者的真正区别在于报文段序列是交叉的还是顺序的。
4 TCP状态转换
在一个TCP连接生命周期内,运行在每台主机中的TCP协议在各种TCP状态(TCP state)之间变迁。
如下图说明了客户TCP会经历的一系列的TCP状态。
为了更容易理解,下面会给出三次握手和四次挥手的图示。
客户TCP开始时处于CLOSE(关闭)状态。客户应用程序发起一个新的TCP连接、这引起客户中的TCP发送一个SYN报文段。在发送完报文段后,客户TCP进入SYN_SENT(同步已发送)状态。在SYN_SENT状态时,它等待来自服务器TCP对客户所发送的SYN报文段进行确认并且SYN比特被置为1的一个报文段。收到这样一个报文段后,客户TCP进入ESTABLISHED(已建立)状态。
假设客户应用进程决定要关闭这个连接(服务器也可以关闭该连接),这引起客户TCP发送一个带有FIN比特被置为1的报文段,并进入FIN_WAIT_1(终止等待1)状态。当处在FIN_WAIT_1状态时,客户TCP等待一个来自服务器带有确认的TCP报文段,当它收到该报文段时,客户TCP进入FIN_WAIT_2(终止等待2)状态。当处在FIN_WAIT_2状态时,客户TCP等待一个来自服务器FIN被置为1的另一个报文段。当收到该报文段后,客户TCP对服务器的报文段进行确认,并进入TIME_WAIT(计时等待)状态。假如ACK丢失,TIME_WAIT状态使TCP客户重传最后的确认报文。等待2MSL时间后,客户TCP进入CLOSE状态,客户端所有资源(包括端口号)将被释放。
下图说明了服务器端TCP会经历的一系列的TCP状态。分析过程和上面类似,这里不再介绍。
5 TCP的四种计时器
对每个连接,TCP管理4个不同的计时器:重传计时器、持续计时器、时间等待(2MSL)计时器、保活计时器。
(1) 重传计时器:发送方发送一个报文段后启动重传计时器,在规定的时间内,如果发送方没有收到接收方对于已发送的报文段的确认时,发送方将会重新发送未收到确认的报文段。
(2) 持续计时器:TCP连接一方收到另一方的零窗口通知时,则启动持续计时器,如果计时器超时,则发送方会发送一个零窗口探测报文段,用于检测接收方发送的非零窗口通知是否丢失。
(3) 时间等待计时器:在TCP发起释放连接方(如发送端)接收到另一方(服务器端)的关闭连接请求后,发送确认关闭报文段后,启动时间等待计时器,等待2MSL(2个最长报文寿命)后才能关闭连接,用于防止客户端返回对服务器段请求关闭连接的报文段的确认报文段丢失。
前面3个计时器前面都已经介绍过了。
(4) 保活计时器(keepalive timer):用于服务器检测客户端是否在服务器不知情的情况下出现故障断开连接了。
假设这样的情况,客户已主动与服务器建立了TCP连接,但是后来客户端的主机突然出现故障。显然,服务器以后就不能再收到客户发来的数据。因此,应当有措施使服务器不要再白白等待下去,这时就是使用保活计时器。
服务器没收到一次客户的数据,就重新设置保活计时器,时间通常是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔75秒发送一次,如果连续10个探测报文段仍无客户的响应,服务器就认为客户端出了故障,接着就关闭了连接。
前面提到半开启的连接的问题,如果一个TCP连接是半开启连接,即客户端再服务器端不知情的情况下断开了连接,那么在保活计时器超时后,就会探测出客户端已经断开了连接,这时服务器就会释放这个TCP连接的所有资源,这样上面所提到的问题就能得到很好的解决。