计算机网络5——运输层3TCP实现

一、TCP 可靠传输的实现

我们首先介绍以字节为单位的滑动窗口。为了讲述可靠传输原理的方便,我们假定数据传输只在一个方向进行,即A发送数据,B给出确认。这样的好处是使讨论限于两个窗口,即发送方A的发送窗口和接收方B的接收窗口。如果再考虑B也向A发送数据,那么还要增加 A的接收窗口和B的发送窗口,这样总共有4个都不断在变化大小的窗口。这对讲述可靠传输的原理并没有多少帮助,反而会使问题变得更加烦琐。

1、以字节为单位的滑动窗口

TCP的滑动窗口是以字节为单位的。为了便于说明滑动窗口的工作原理,我们故意把后面图中的字节编号都取得很小(实际的窗口大小多为数千字节)。现假定A收到了B发来的确认报文段,其中窗口是20字节,而确认号是31(这表明B期望收到的下一个字节序号是31(请注意,这里不是分组的序号),而到序号30为止的数据已经收到了)。根据这两个数据,A 就构造出自己的发送窗口,如图 5-14 所示。
在这里插入图片描述
我们先讨论发送方A的发送窗口。发送窗口表示:在没有收到B的确认的情况下,A可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。

发送窗口里面的序号表示允许发送的序号。显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。在上面我们已经讲过,接收方会把自己的接收窗口数值放在窗口字段中发送给对方。因此,A的发送窗口一定不能超过 B的接收窗口数值。在后面我们将要讨论,发送方的发送窗口大小还要受到当时网络拥塞程度的制约。但在目前,我们暂不考虑网络拥塞的影响。

发送窗口后沿的后面部分表示已发送且已收到了确认。这些数据显然不需要再保留了而发送窗口前沿的前面部分表示不允许发送,因为接收方没有为这部分数据保留临时存放的缓存空间。

发送窗口的位置由窗口前沿和后沿的位置共同确定。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销掉已收到的确认。发送窗口前沿通常是不断向前移动的,但也有可能不动。这对应两种情况:一是没有收到新的确认,对方通知的窗口大小也不变;二是收到了新的确认但对方通知的窗口缩小了,使得发送窗口前沿正好不动。

发送窗口前沿也有可能向后收缩。这发生在对方通知的窗口缩小了。但TCP的标准强烈不赞成这样做。因为很可能发送方在收到这个通知以前已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,这样就会产生一些错误。

现在假定 A发送了序号为31~41的数据。这时,发送窗口位置并未改变(如下图所示),但发送窗口内靠后面有11个字节(灰色方框表示)表示已发送但未收到确认。而发送窗口内靠前面的9个字节(序号42~50)是允许发送但尚未发送的。
在这里插入图片描述
从以上所述可以看出,要描述一个发送窗口的状态需要三个指针:P1,P2和P3(如上图所示)。指针都指向字节的序号。A的发送窗口中三个指针指向的几个部分的意义如下:

  • P1之前的数据(序号<31)是已发送并已收到确认的部分。
  • P3之后的数据(序号>50)是不允许发送的部分。
  • P3-P1 =A的发送窗口=20(序号 31~50)。
  • P2-P1=已发送但尚未收到确认的字节数(序号 31~41)。
  • P3-P2=允许发送但当前尚未发送的字节数(序号42~50)(又称为可用窗口或有效窗口)。

再看一下B的接收窗口。设B的接收窗口大小是20。在接收窗口外面,到序号为30的数据是已经发送过确认,并且已经交付主机了。因此在B可以不再保留这些数据。接收窗口内的数据(序号31~50)是允许接收的。在上图中,B收到了序号为32和33的数据,但序号为31的数据没有收到(也许丢失了,也许滞留在网络中的某处)。请注意,B只能对按序收到的数据中的最高序号给出确认,因此B发送的确认报文段中的确认号仍然是31(即期望收到的序号)。

现在假定B收到了序号为31的数据,把序号为31~33的数据交付主机,删除这些数据。接着把接收窗口向前移动3个序号(如下图所示),同时给A发送确认,其中窗口值仍为 20,但确认号是34。这表明B已经收到了到序号33 为止的数据。我们注意到,B还收到了序号为37、38和40的数据,但这些数据都没有按序到达,只能先暂存在接收窗口中。A收到B的确认后,就可以把发送窗口向前滑动3个序号,但指针P不动。可以看出现在A的可用窗口增大了些,可发送的序号范围是42~53。

A在继续发送完序号42~53的数据后,指针P2向前移动和P3重合。发送窗口内的序号都已用完,但还没有再收到确认(如下图所示)。由于A的发送窗口已满,可用窗口已减小到零,因此必须停止发送。请注意,存在下面这种可能性,就是发送窗口内所有的数据都已正确到达 B,B也早已发出了确认。但不幸的是,所有这些确认都滞留在网络中。在没有收到B的确认时,为了保证可靠传输,A只能认为B还没有收到这些数据。于是,A在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,直到收到B的确认为止。如果A按序收到落在发送窗口内的确认号,那么A就可以使发送窗口继续向前滑动,并发送新的数据。
在这里插入图片描述
我们在前面曾给出了这样的概念:发送方的应用进程把字节流写入TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流。下面我们就进一步讨论前面讲的窗口和缓存的关系。下图画出了发送方维持的发送缓存和发送窗口,以及接收方维持的接收缓存和接收窗口。这里首先要明确两点:
在这里插入图片描述

  • 第一,缓存空间和序号空间都是有限的,并且都是循环使用的。最好是把它们画成圆环状的。但这里为了画图的方便,我们还是把它们画成了长条状的。
  • 第二,由于缓存或窗口中实际的字节数可能很大,因此上图仅仅是个示意图,没有标出具体的数值。但用这样的图来说明缓存和发送窗口以及接收窗口的关系是很清楚的。

我们先看一下图 (a)所示的发送方的情况。
发送缓存用来暂时存放:

  • 发送应用程序传送给发送方TCP准备发送的数据
  • TCP 已发送出但尚未收到确认的数据。

发送窗口通常只是发送缓存的一部分。已被确认的数据应当从发送缓存中删除,因此发送缓存和发送窗口的后沿是重合的。发送应用程序最后写入发送缓存的字节减去最后被确认的字节,就是还保留在发送缓存中的被写入的字节数。发送应用程序必须控制写入缓存的速率,不能太快,否则发送缓存就会没有存放数据的空间。

再看一下图(b)所示的接收方的情况。
接收缓存用来暂时存放:

  • 按序到达的、但尚未被接收应用程序读取的数据
  • 未按序到达的数据。

如果收到的分组被检测出有差错,则要丢弃。如果接收应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到零。反之,如果接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,但最大不能超过接收缓存的大小。

图(b)中还指出了下一个期望收到的字节号。这个字节号也就是接收方给发送方的报文段的首部中的确认号。
根据以上所讨论的,我们还要再强调以下三点。

  • 第一,虽然A的发送窗口是根据B的接收窗口设置的,但在同一时刻,A的发送窗口并不总是和B的接收窗口一样大。这是因为通过网络传送窗口值需要经历一定的时间滞后(这个时间是不确定的)。另外,正如后面5.7节将要讲到的,发送方A还可能根据网络当时的拥塞情况适当减小自己的发送窗口数值。
  • 第二,对于不按序到达的数据应如何处理,TCP标准并无明确规定。如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利(因为发送方会重复传送较多的数据)。因此TCP通常是把不按序到达的数据先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。
  • 第三,TCP要求接收方必须有累积确认的功能,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。但请注意两点。一是接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络的资源。TCP标准规定,确认推迟的时间不应超过0.5秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认[RFC 1122,STD3]。二是捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。

最后再强调一下,TCP的通信是全双工通信。通信中的每一方都在发送和接收报文段因此,每一方都有自己的发送窗口和接收窗口。在谈到这些窗口时,一定要弄清是哪一方的窗口。

2、超时重传时间的选择

上面已经讲到,TCP的发送方在规定的时间内没有收到确认就要重传已发送的报文段这种重传的概念是很简单的,但重传时间的选择却是TCP最复杂的问题之一。

由于 TCP的下层是互联网环境,发送的报文段可能只经过一个高速率的局域网,也可能经过多个低速率的网络,并且每个IP数据报所选择的路由还可能不同。如果把超时重传时间设置得太短,就会引起很多报文段的不必要的重传,使网络负荷增大。但若把超时重传时间设置得过长,则又使网络的空闲时间增大,降低了传输效率。

那么,运输层的超时计时器的超时重传时间究竟应设置为多大呢?
TCP采用了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间 RTT。TCP保留了RTT的一个加权平均往返时间 RTTS(这又称为平滑的往返时间,8表示 Smoothed。因为进行的是加权平均,因此得出的结果更加平滑)。每当第一次测量到 RTT 样本时,RTT,值就取为所测量到的 RTT 样本值。但以后每测量到一个新的RTT样本,就按下式重新计算一次RTTS:
在这里插入图片描述
在上式中,0≤ α≤1。若α很接近于零,表示新的 RTTS值和旧的 RTTS值相比变化不大,而对新的RTT样本影响不大(RTT值更新较慢)。若选择a接近于1,则表示新的RTTs值受新的 RTT 样本的影响较大(RTT值更新较快)。已成为建议标准的RFC6298 推荐的α值为 1/8,即 0.125。用这种方法得出的加权平均往返时间 RTTS就比测量出的 RTT 值更加平滑。

显然,超时计时器设置的超时重传时间 RTO(RetransmissionTime-Out)应略大于上面得出的加权平均往返时间 RTTS。RFC 6298 建议使用下式计算RTO:
在这里插入图片描述
而RTTD是RTT的偏差的加权平均值,它与RTT、和新的RTT样本之差有关。RFC6298 建议这样计算 RTTD。当第一次测量时,RTT值取为测量到的 RTT 样本值的一半。在以后的测量中,则使用下式计算加权平均的 RTTD:
在这里插入图片描述
这里B是个小于1的系数,它的推荐值是1/4,即0.25。

上面所说的往返时间的测量,实现起来相当复杂。试看下面的例子。

如下图所示,发送出一个报文段,设定的重传时间到了,还没有收到确认,于是重传报文段。经过了一段时间后,收到了确认报文段。现在的问题是:如何判定此确认报文段是对先发送的报文段的确认,还是对后来重传的报文段的确认?由于重传的报文段和原来的报文段完全一样,因此源主机在收到确认后,就无法做出正确的判断,而正确的判断对确定加权平均 RTTs的值关系很大。

若收到的确认是对重传报文段的确认,但却被源主机当成是对原来的报文段的确认,则这样计算出的 RTTS和超时重传时间 RTO就会偏大。若后面再发送的报文段又是经过重传后才收到确认报文段,则按此方法得出的超时重传时间RTO 就越来越长。
在这里插入图片描述
同样,若收到的确认是对原来的报文段的确认,但被当成是对重传报文段的确认,则由此计算出的 RTTS和RTO都会偏小。这就必然导致报文段过多地重传。这样就有可能使RTO 越来越短。

根据以上所述,Kam 提出了一个算法:在计算加权平均 RTTS时,只要报文段重传了就不采用其往返时间样本。这样得出的加权平均 RTTs和 RTO 就较准确。

但是,这又引起新的问题。设想出现这样的情况:报文段的时延突然增大了很多。因此在原来得出的重传时间内不会收到确认报文段,于是就重传报文段。但根据Kam算法,不考虑重传的报文段的往返时间样本。这样,超时重传时间就无法更新。

因此要对 Kam 算法进行修正。方法是:报文段每重传一次,就把超时重传时间 RTO增大一些。典型的做法是取新的重传时间为旧的重传时间的2倍。当不再发生报文段的重传时才根据上面给出的式(5-5)计算超时重传时间。实践证明,这种策略较为合理。

总之,Karn 算法能够使运输层区分开有效的和无效的往返时间样本,从而改进了往返时间的估测,使计算结果更加合理。

3、选择确认SACK

现在还有一个问题没有讨论。这就是若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?答案是可以的。选择确认(Selective ACK)[RFC 2018,建议标准]就是一种可行的处理方法。

我们用一个例子来说明选择确认的工作原理。TCP的接收方在接收对方发送过来的数据字节流的序号不连续,结果就形成了一些不连续的字节块(如图5-20所示)。可以看出,序号1~1000收到了,但序号1001~1500没有收到。接下来的字节流又收到了,可是又缺少了3001~3500。再后面从序号4501起又没有收到。也就是说,接收方收到了和前面的字节流不连续的两个字节块。如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。
在这里插入图片描述
从上图可看出,和前后字节不连续的每一个字节块都有两个边界:左边界和右边界,因此在图中用四个指针标记这些边界。请注意,第一个字节块的左边界L1=1501,但右边界R1=3001而不是3000。这就是说,左边界指出字节块的第一个字节的序号,但右边界减1才是字节块的最后一个序号。同理,第二个字节块的左边界L=3501,而右边界R=4501。

我们知道,TCP的首部没有哪个字段能够提供上述这些字节块的边界信息。RFC 2018规定,如果要使用选择确认SACK,那么在建立TCP连接时,就要在TCP首部的选项中加上“允许 SACK”的选项,而双方必须都事先商定好。如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在TCP报文段的首部中都增加了SACK 选项以便报告收到的不连续的字节块的边界。由于首部选项的长度最多只有40字节,而指明一个边界就要用掉4字节(因为序号有32位,需要使用4个字节表示),因此在选项中最多只能指明4个字节块的边界信息。这是因为4个字节块共有8个边界,因而需要用32个字节来描述。另外还需要两个字节,一个字节用来指明是SACK选项,另一个字节指明这个选项要占用多少字节。如果要报告5个字节块的边界信息,那么至少需要42个字节。这就超过了选项长度 40字节的上限。互联网建议标准RFC2018还对报告这些边界信息的格式都做出了非常明确的规定,这里从略。

然而,SACK文档并没有指明发送方应当怎样响应SACK。因此大多数的实现还是重传所有未被确认的数据块。

二、TCP 的流量控制

1、利用滑动窗口实现流量控制

·般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制(fowcontrol)就是让发送方的发送速率不要太快,要让接收方来得及接收。

利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。

下面通过下图的例子说明如何利用滑动窗口机制进行流量控制。
在这里插入图片描述
设A向B发送数据。在连接建立时,B告诉了A:“我的接收窗口rwnd=400。”(这里rwnd 表示 receiver window。)因此,发送方的发送窗口不能超过接收方给出的接收窗口"的数值。请注意,TCP的窗口单位是字节,不是报文段。TCP连接建立时的窗口协商过程在图中没有显示出来。再设每一个报文段为100字节长,而数据报文段序号的初始值设为1(见图中第一个箭头上面的序号seq=1。图中右边的注释可帮助理解整个过程)。请注意,图中箭头上面大写 ACK 表示首部中的确认位 ACK,小写 ack 表示确认字段的值。

我们应注意到,接收方的主机B进行了三次流量控制。第一次把窗口减小到rwnd = 300,第二次又减到rwnd=100,最后减到rwnd=0,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机B重新发出一个新的窗口值为止。我们还应注意到,B向A发送的三个报文段都设置了ACK=1,只有在ACK=1时确认号字段才有意义。

现在我们考虑一种情况。在上图中,B向A发送了零窗口的报文段后不久,B的接收缓存又有了一些存储空间。于是B向A发送了rwnd=400的报文段。然而这个报文段在传送过程中丢失了。A 一直等待收到B发送的非零窗口的通知,而B也一直等待A发送的数据。如果没有其他措施,这种互相等待的死锁局面将一直延续下去。

为了解决这个问题,TCP 为每一个连接设有一个持续计时器(persistence timer)。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。如果窗口仍然是零,那么收到这个报文段的一方就重新设置持续计时器。如果窗口不是零,那么死锁的僵局就可以打破了。

2、TCP的传输效率

前面已经讲过,应用进程把数据传送到TCP的发送缓存后,剩下的发送任务就由TCP来控制了。可以用不同的机制来控制TCP 报文段的发送时机。

  • 第一种机制是 TCP维持一个变量,它等于最大报文段长度MSS只要缓存中存放的数据达到MSS字节时,就组装成一个TCP报文段发送出去。
  • 第二种机制是由发送方的应用进程指明要求发送报文段,即TCP支持的推送(push)操作。
  • 第三种机制是发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过MSS)发送出去。

但是,如何控制 TCP发送报文段的时机仍然是一个较为复杂的问题。

例如,一个交互式用户使用一条TELNET连接(运输层为TCP协议)。假设用户只发1个字符,加上 20字节的首部后,得到21字节长的TCP报文段。再加上 20字节的IP首部形成 41字节长的IP数据报。在接收方TCP立即发出确认,构成的数据报是 40字节长(假定没有数据发送)。若用户要求远地主机回送这一字符,则又要发回41字节长的IP 数据报和 40 字节长的确认IP 数据报。这样,用户仅发1个字符时,线路上就需传送总长度为162字节共4个报文段。当线路带宽并不富裕时,这种传送方法的效率的确不高。因此应适当推迟发回确认报文,并尽量使用捎带确认的方法。

在TCP的实现中广泛使用Nagle算法。算法如下:若发送应用进程把要发送的数据逐个字节地送到 TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据到达较快而网络速率较慢时,用这样的方法可明显地减少所用的网络带宽。Nagle算法还规定,当到达的数据已达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。这样做,就可以有效地提高网络的吞吐量。

另一个问题叫作糊涂窗口综合征(sily window syndrome),有时也会使 TCP 的性能变坏设想一种情况:TCP接收方的缓存已满,而交互式的应用进程一次只从接收缓存中读取1个字节(这样就使接收缓存空间仅腾出1个字节),然后向发送方发送确认,并把窗口设置为1个字节(但发送的数据报是40字节长)。接着,发送方又发来1个字节的数据(请注意发送方发送的IP数据报是 41字节长)。接收方发回确认,仍然将窗口设置为1个字节。这样进行下去,使网络的效率很低。

要解决这个问题,可以让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段,或者等到接收缓存已有一半空闲的空间。只要出现这两种情况之一,接收方就发出确认报文,并向发送方通知当前的窗口大小。此外,发送方也不要发送太小的报文段,而是把数据积累成足够大的报文段,或达到接收方缓存的空间的一半大小。上述两种方法可配合使用。使得在发送方不发送很小的报文段的同时,接收方也不要在缓存刚刚有了一点小的空间就急忙把这个很小的窗口大小信息通知给发送方。

上述两种方法可配合使用。使得在发送方不发送很小的报文段的同时,接收方也不要在缓存刚刚有了一点小的空间就急忙把这个很小的窗口大小信息通知给发送方。

  • 27
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值