TCP/IP学习之 TCP拥塞控制与定时器

来源:互联网 发布:nginx clientbodytemp 编辑:程序博客网 时间:2024/04/20 14:14

TCP的交互数据流和成块数据流

经受时延的确认
为什么要分这两类呢?如果按照分析数量来计算,约有一半的TCP报文段包含成块数据(FTP、电子邮件),另一半则包含交互数据(Telnet和Rlogin)。成块的报文段基本上都是满长度的,而交互数据则小的多,可能一个交互过程只发送一个字节的数据,而需要传输20个IP的头,20个字节的TCP的头,这个时候网络的利用率就会很低。例如Rlogin需要远程系统回显,那么每次键入一个字符都会产生4个报文段,为(1)C(client)到S(server)的数据字节(2)S到C的对数据字节的确认(3)S到C的数据字节的回显(4)C到S的对回显的确认。然而我们可以将报文段2和报文段3进行合并,这样就会提高网络的利用率。这种合并的技术称为经受时延的确认。

在局域网上,这种小的分组通常不回引起麻烦,因为局域网一般不会出现阻塞,但在广域网上,这些小的分组会增加拥塞的可能,一种简单的好的方法就是采用Nagle算法。该算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些少量的分组,并在确认到来时以一个分组的方式发送出去。该算法的优越之处在于它是自适应的:确认到达的越快,数据也就发送的越快。有时候我们也需要关闭Nagle算法,例如窗口系统服务器:小的消息(鼠标移动)必须无时延的发送。

慢启动
对于成块的数据流,TCP有一种“慢启动(slow star)”的算法。如果一开始就向网络发送多个报文段,可能会使得中间的一些路由器耗尽缓存的空间,会严重的影响TCP连接的吞吐量。慢启动通过观察到新分组进入网络的速率应该与另一端确认的速率相同而进行工作。慢启动为发送方的TCP增加另一个窗口:拥塞窗口(congestion window),记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段,每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口是接收方使用的流量控制。发送方开始时发送一个报文段,然后等待ACK当收到该ACK时,拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的ACK时,拥塞窗口就增加为4,这是一种指数增加的关系。

拥塞避免
慢启动算法是在一个连接上发起数据流的方法,但有时候我们会达到中间路由器的极限,此时分组将被丢弃。拥塞避免算法是一种处理丢失分组的算法。拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希望降低分组进入网络的传出速率。于是可以调用慢启动来做到这一点。在实际中这两个算法通常在一起实现。
拥塞避免算法和慢启动算法都需要对每个连接维持两个变量:一个拥塞窗口cwnd和一个慢启动门限ssthresh。算法过程如下:
1)对于一个给定的连接,初始化cwnd为1个报文段ssthread为65535个字节。
2)TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。
3)当拥塞发生的时候(超时和收到重复确认(连续收到三个ack,无需等待超时定时器,会执行快速恢复和快速重传)),ssthresh被设置为当前窗口大小的一半(cwnd和通告窗口大小的最小值,但最少为2个报文段)。此外,如果是超时引起了拥塞,则cwnd被设置为1个报文段(慢启动)。
4)当新的数据被对方确认的时候,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到我们回到拥塞发生时所处位置一半的时候(也就是当前ssthresh的值),然后转为拥塞避免。
慢启动算法初始设置cwnd为一个报文段,此后每收到一个确认就加1,为指数增长。拥塞避免算法要求每次收到一个确认时将cwnd增加(1/cwnd)。与慢启动的指数增加比起来,这时一种加性增长。

注意并不是cwnd一直增长,然后发生了发送过多报文拥塞,在执行拥塞避免算法重复的过程。发送报文的多少还要看对方的通告窗口大小,即使cwnd很大,网络性能很差,但是对方每次的通告窗口小,即对方处理的慢,也可能不发生拥塞。

快速重传与快速恢复
在收到一个失序的报文段时,TCP立即需要产生一个ACK(一个重复的ACK)。这个ACK不应该被迟延。该重复的ACK的目的在于让对方知道收到了一个失序的报文段,并告诉对方自己希望收到的序号。由于我们不知道重复的ACK是由一个丢失的报文段引起的,还是由于仅仅出现了几个报文段的重新排序,因此我们等待少量重复的ACK到来。加入这只是一些报文段的重新排序,则在重新排序的报文段处理并产生一个新的ACK之前,只可能是1到2个重复的ACK。如果一连串收到3个或三个以上重复的ACK就非常可能是一个报文段丢失了。于是我们就重传丢失的数据报文段,而无需等待超时定时器溢出。这就是快速重传算法。接下来执行的不是慢启动算法而是拥塞避免算法,这就是快速恢复算法。

紧急方式
TCP提供了“紧急方式(urgent mode)”,它使一端可以告诉另一端有些具有某种方式“紧急数据”已经放置在普通的数据流中。另一端被通知这个紧急数据已经放置在普通数据流中,由接受方决定如何处理。可以通过TCP首部中的两个字段发出这种从一端到另一端的紧急数据已经被放置在数据流中的通知。URG比特被置为1,并且一个16bit的紧急指针被置为一个正的偏移量,该偏移量必须与TCP首部中的序号字段相加,以便得出紧急数据的最后一个字节的序号。一个比较常见的例子就是telnet和ftp用户中断传输的时候就是用的这个。

TCP的超时与重传
TCP发送数据的时候可能丢失,TCP通过设置一个定时器来解决这个问题。如果当定时器溢出时还有收到确认,他就重传数据。对于每个连接,TCP管理4个不同的定时器。
1)重传定时器使用于当希望收到另一端的确认。
2)坚持(persist)定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口。
3)保活(keepalive)定时器可检测到一个空闲连接的另一端何时崩溃或重启。
4)2MSL定时器测量一个连接处于TIME_WAIT状态的时间。

TCP的坚持定时器
为什么要有坚持定时器呢?在TCP的连接中会有这样一种情况,TCP通过让接收方指明希望从发送方接收的数据字节(即窗口大小)来进行流量控制。如果窗口的大小为0,那么将有效的阻止发送方传送数据,直到窗口为0为止。这个时候如果接收方发送通告了一个非0的窗口,如果这个ACK丢失了(没有数据报文段是不需要确认的),这个时候接收方没有对这个ACK启动重传定时器。如果丢失了,那么接收方会一直等待数据到来,而发送方由于ACK丢失,没有收到非0的窗口还认为接收方不能接收数据,一直也不会发送数据。这个时候就会陷入死锁。为了防止这种种情况发生,发送方使用一个坚持定时器来周期性地向接收方查询,以便发现窗口是否已增大。

TCP保活定时器
保活定时器主要是为服务器应用程序提供的,服务器应用程序希望知道客户主机是否崩溃,从而可以代表客户使用资源。保活的功能就是试图在服务端检测到这种半开放的连接。如果一个给定的连接在两个小时内没有任何动作,则服务器就向客户发送一个探查报告文段,客户机的状态有以下几种
1)客户机依然正常,从服务器可达,服务器在两个小时之后将保活定时器复位。
2)客户主机已经崩溃,关闭或者正在重启。这个时候客户的TCP没有响应,此时服务器每75秒发送一个探查,如果服务器没有收到任何一个响应,它就认为客户主机已经关闭并终止连接。
3)客户主机崩溃并应经重新启动。这个时候服务器将收到一个复位,使得服务器终止这个连接。
4)客户主机正常但是不可到达,这个时候对于服务器来说与2情况是一样的。

0 0