TCP定时器
1. TCP中7种定时器
TCP中有7中定时器
(1)建立连接定时器(connection-establishment timer)
(2)重传定时器(retransmission timer)
(3)延迟应答定时器(delayed ACK timer)
(4)坚持定时器(persist timer)
(5)保活定时器(keepalive timers)
(6)FIN_WAIT_2定时器(FIN_WAIT_2 timer)
(7)TIME_WAIT定时器(TIME_WAIT timer,也叫2MSL timer)
2. 建立连接定时器(connection-establishment timer)
这个定时器是在建立连接的时候使用的,TCP建立连接需要3次握手,如图:
建立连接的过程中,在发送SYN时,会启动一个定时器(默认应该是3秒),如果SYN包丢失了,那么3秒以后会重新发送SYN包(当然还会启动一个新的定时器,设置成6秒超时),当然也不会一直没完没了的发SYN包,在/proc/sys/net/ipv4/tcp_syn_retries可以设置到底要重新发送几次SYN包。如图:
3. 重传定时器(retransmission timer)
重传定时器在TCP发送数据时设定,在计时器超时后没有收到返回的确认ACK,发送端就会重新发送队列中需要重传的报文段。使用RTO重传计时器一般有如下规则:
(1)当TCP发送了位于发送队列最前端的报文段后就启动这个RTO计时器
(2)如果队列为空则停止计时器,否则重启定时器
(3)当计时器超时后,TCP会重传发送队列最前端的报文段
(4)当一个或多个报文段被累计确认后,这个货这些报文段会被清除出队列
重传计时器保证了接收端能够接收到丢失的报文段,继而保证了接收端交付给接收进程的数据始终是有序完整的。因为接收端永远不会把一个失序不完整的报文段交付给接收进程。
4. 延迟应答定时器(delayed ACK timer)
延迟应答也被称为捎带ACK,这个定时器是在延迟应答的时候使用的。为什么要延迟应答呢?延迟应答是为了提供网络传输的效率。
举例说明,比如服务端收到客户端的数据后,不是立刻回ACK给客户端,而是等一段时间(一般最大200ms),这样如果服务端要是有数据需要发给客户端,那么这个ACK就和服务端的数据一起发给客户端了,这样比立即给客户端一个ACK节省了一个数据包。
5. 坚持定时器(persist timer)
TCP通过让接收方指明希望从发送方接收的数据字节数(即窗口大小)来进行流量控制。如果窗口大小为0会发生什么情况呢?这将有效地阻止发送方传送数据,直到窗口变为非0为止。接收端窗口变为非0后,就会发送一个确认ACK指明需要的报文段序号以及窗口大小。
如果这个确认ACK丢失了,则双方就有可能因为等待对方而使连接终止:接收方等待接收数据(因为它已经向发送方通告了一个非0的窗口),而发送方在等待允许它继续发送数据的窗口更新。为防止这种死锁情况的发生,发送方使用一个坚持定时器(persist timer)来周期性地向接收方查询,以便发现窗口是否已增大。这些从发送方发出的报文段称为窗口探查(window probe)。
6. 保活定时器(keepalive timer)
许多TCP/IP的初学者会很惊奇地发现可以没有任何数据流通过一个空闲的TCP连接,也就是说,如果TCP连接的双方都没有向对方发送数据,则在两个TCP模块之间不交换任何信息。
两个应用进程--客户进程或服务器进程都没有使用应用级的定时器来检测非活动状态,而这种非活动状态可以导致应用进程中的任何一个终止其活动。
许多时候服务器希望知道客户主机是否崩溃并关机或崩溃又重新启动,许多实现提供的保活定时器可以提供这种能力。
保活功能主要是为服务器应用程序提供的,服务器应用希望知道客户主机是否崩溃。保活功能试图在服务器端检测到这种半开放的连接。
如果一个给定的连接在两个小时之内没有任何动作,则服务器就向客户发送一个探查报文段,客户主机必须处于一下4个状态之一:
(1)客户主机依然正常运行,并从服务器可达。客户端TCP响应正常,而服务器也知道对方是正常工作的。服务器在两个时以后将保活定时器复位。如果在两个小时定时器到时间之前有应用程序的通信量通过此链接,则定时器在交换数据后的未来2小时再复位
(2)客户主机已经崩溃,并且关闭或正在重新启动。在任何一种情况下,客户的TCP都没有响应,服务器将不能够收到探查的响应。--会返回诸如“连接超时”之类的信息。
(3)客户主机崩溃并已经重新启动。这时服务器将收到一个对其保活探查的响应,但是这个响应是一个复位,使得服务器终止这个连接。 -- 会返回诸如“连接被对方复位”
(4)客户主机正常运行,但是从服务器不可达。与(2)相同。
在TCP连接建立的时候指定了SO_KEEPALIVE,保活定时器才会有效。如果客户端和服务端长时间没有数据交互,那么需要保活定时器来判断是否对端还活着,但是这个其实很不实用,因为默认是2小时没有数据交互才探测,时间实在是太长了。如果你真的要确认对端是否活着,那么应该自己实现心跳包,而不是依赖于这个保活定时器。
7. FIN_WAIT_2定时器(FIN_WAIT_2 timer)
主动关闭的一段调用完close之后(即发FIN给被动关闭的一段,并且收到其对FIN的确认ACK)则进入FIN_WAIT_2状态。如果这个时候因为网络突然断掉,被动关闭的一段宕机等原因,导致主动关闭的一段不能收到被动关闭的一段发来的FIN,主动关闭的一端总不能一直等着,占用资源不释放吧。这个时候就需要FIN_WAIT_2定时器出马了,如果在该定时器超时的时候,还是没收到被动关闭一端发来的FIN,那么不好意思,不等了,直接释放这个连接。FIN_WAIT_2定时器的时间可以从/proc/sys/net/ipv4/tcp_fin_timeout中查看和设置。如图:
8. TIME_WAIT定时器(TIME_WAIT timer,也叫2MSL timer)
TIME_WAIT是主动关闭连接的一端之后进入的状态,而不是直接变成CLOSED的状态,为什么呢?
(1)万一被动关闭的一端在超时时间内没有收到最后一个ACK,则会重发最后的FIN,2MSL(报文段最大生存时间)等待时间保证了重发的FIN会被主动关闭的一端收到且重新发送最后一个ACK
(2)另一个原因是在2MSL等待时间内,任何迟到的报文段会被接收并丢弃,防止老的TCP连接的包在新的TCP连接里出现。不可避免,在这个2MSL等待时间呢,不会建立同样(源IP、源端口、目的IP、目的端口)的连接。