传输层 III(TCP协议——可靠传输)【★★★★】
(★★)代表非常重要的知识点,(★)代表重要的知识点。
一、可靠传输的工作原理
我们知道, TCP 发送的报文段是交给 IP 层传送的。但 IP 层只能提供尽最大努力服务,也就是说, TCP 下面的网络所提供的是不可靠的传输。因此, TCP 必须采用适当的措施才能使得两个运输层之间的通信变得可靠。
理想的传输条件有以下两个特点:
- 传输信道不产生差错。
- 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。
在这样的理想传输条件下,不需要采取任何措施就能够实现可靠传输。
然而实际的网络都不具备以上两个理想条件。但我们可以使用一些可靠传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当降低发送数据的速度。这样一来,本来不可靠的传输信道就能够实现可靠传输了。
1. 停止-等待协议
全双工通信的双方既是发送方也是接收方。下面为了讨论问题的方便,我们仅考虑 A 发送数据而 B 接收数据并发送确认。因此 A 叫做发送方,而 B 叫做接收方。因为这里是讨论可靠传输的原理,因此把传送的数据单元都称为分组,而并不考虑数据是在哪一个层次上传送的。“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
注:运输层传送的协议数据单元叫做报文段,网络层传送的协议数据单元叫做 IP 数据报。但在一般讨论问题时,都可把它们简称为分组。
在计算机网络发展初期,通信链路不太可靠,因此在链路层传送数据时都要采用可靠的通信协议。其中最简单的协议就是“停止-等待协议”。然而在传输层并不使用这种协议,这里只是为了引出可靠传输的问题才从最简单的概念讲起。在传输层使用的可靠传输协议要复杂得多。
1)无差错情况
停止-等待协议可用下图来说明。下图(a)是最简单的无差错情况。A 发送分组 M1 ,发完就暂停发送,等待 B 的确认。B 收到了 M1 就向 A 发送确认。A 在收到了对 M1 的确认后,就再发送下一个分组 M2 。同样,在收到 B 对 M2 的确认后,再发送 M3 。
2)出现差错情况
上图(b)是分组在传输过程中出现差错的情况。B 接收 M1 时检测出了差错,就丢弃 M1 ,其他什么也不做(不通知 A 收到有差错的分组)。也可能是 M1 在传输过程中丢失了,这时 B 当然什么都不知道。在这两种情况下,B 都不会发送任何信息。
可靠传输协议是这样设计的:A 只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫做超时重传。要实现超时重传,就要在每发送完一个分组时设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销己设置的超时计时器。其实在上图(a)中, A 为每一个已发送的分组都设置了一个超时计时器。但 A 只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器。
注:在可靠传输的协议中,也可以在检测出有差错时发送“否认报文”给对方。这样做的好处是能够让发送方及早知道出现了差错。不过由于这样处理会使协议复杂化,现在实用的可靠传输协议都不使用这种否认报文了。
这里应注意以下三点:
-
第一, A 在发送完一个分组后,必须暂时保留已发送的分组的副本(在发生超时重传时使用)。只有在收到相应的确认后才能清除暂时保留的分组副本。
-
第二,分组和确认分组都必须进行编号,这样才能明确是哪一个发送出去的分组收到了确认,而哪一个分组还没有收到确认。
注:编号并不是一个非常简单的问题。分组编号使用的位数总是有限的,同一个号码会重复使用。例如, 10 位的编号范围是 0 ~ 1023 。当编号增加到 1023 时,再增加一个号就又回到 0,然后重复使用这些号码。因此,在所发送的分组中,必须能够区分开哪些是新发送的,哪些是重传的。对于简单链路上传送的帧,如采用停止-等待协议,只要用 1 位编号即可,也就是发送完 0 号帧,收到确认后,再发送 1 号帧,收到确认后,再发送 0 号帧。但是在传输层,这种编号方法有时并不能保证可靠传输
- 第三,超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。上图(b)中的一段虚线表示如果 M1 正确到达 B 同时 A 也正确收到确认的过程。可见重传时间应设定为比平均往返时间更长一些。显然,如果重传时间设定得很长,那么通信的效率就会很低。但如果重传时间设定得太短,以致产生不必要的重传,就浪费了网络资源。然而,在传输层重传时间的准确设定是非常复杂的,这是因为已发送出的分组到底会经过哪些网络,以及这些网络将会产生多大的时延(这取决于这些网络当时的拥塞情况),这些都是不确定因素。
3)确认丢失和确认迟到
下图(a)说明的是另一种情况。B 所发送的对 M1 的确认丢失了。A 在设定的超时重传时间内没有收到确认,并无法知道是自已发送的分组出错、丢失,或者是 B 发送的确认丢失了。因此 A 在超时计时器到期后就要重传 M1 。现在应注意 B 的动作。假定 B 又收到了重传的分组 M1 。这时应采取两个行动。
-
第一,丢弃这个重复的分组 M1 ,不向上层交付。
-
第二,向 A 发送确认。不能认为已经发送过确认就不再发送,因为 A 之所以重传 M1 就表示 A 没有收到对 M1 的确认。
上图(b)也是一种可能出现的情况。传输过程中没有出现差错,但 B 对分组 M1 的确认迟到了。A 会收到重复的确认。对重复的确认的处理很简单:收下后就丢弃。B 仍然会收到重复的 M1 ,并且同样要丢弃重复的 M1 ,并重传确认分组。
通常 A 最终总是可以收到对所有发出的分组的确认。如果 A 不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信。
使用上述的确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信。
像上述的这种可靠传输协议常称为自动重传请求 ARQ(Automatic Repeat reQuest)。意思是重传的请求是自动进行的。接收方不需要请求发送方重传某个出错的分组。
4)信道利用率
停止-等待协议的优点是简单,但缺点是信道利用率太低。我们可以用下图来说明这个问题。为简单起见,假定在 A 和 B 之间有一条直通的信道来传送分组。
假定 A 发送分组需要的时间是 TD 。显然,TD 等于分组长度除以数据率。再假定分组正确到达 B 后,B 处理分组的时间可以忽略不计,同时立即发回确认。假定 B 发送确认分组需要时间 TA 。如果 A 处理确认分组的时间也可以忽略不计,那么 A 在经过时间(TD + RTT + TA)后就可以再发送下一个分组,这里的 RTT 是往返时间。因为仅仅是在时间 TD 内才用来传送有用的数据(包括分组的首部),因此信道的利用率 U 可用下式计算:
U = TD / (TD + RTT + TA)
请注意,更细致的计算还可以在上式分子的时间 TD 内扣除传送控制信息(如首部)所花费的时间。但在进行粗略计算时,用上述式子就可以了。
我们知道,上述式子中的往返时间 RTT 取决于所使用的信道。例如,假定 1200 km 的信道的往返时间 RTT = 20ms 。分组长度是 1200 bit ,发送速率是 1 Mbit/s 。若忽略处理时间和TA(TA 一般都远小于 TD ,则可算出信道的利用率 U = 5.66% 。但若把发送速率提高到 10 Mbit/s, 则 U = 5.96 × 10-3 。信道在绝大多数时间内都是空闲的。
从上图还可看出,当往返时间 RTT 远大于分组发送时间 TD 时,信道的利用率就会非常低。还应注意的是,上图并没有考虑出现差错后的分组重传。若出现重传,则对传送有用的数据信息来说,信道的利用率就还要降低。
为了提高传输效率,发送方可以不使用低效率的停止-等待协议,而是采用流水线传输(如下图所示)。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认。这样可使信道上一直有数据不间断地在传送。显然,这种传输方式可以获得很高的信道利用率。
当使用流水线传输时,就要使用下面介绍的连续 ARQ 协议和滑动窗口协议。
2. 连续 ARQ 协议
滑动窗口协议比较复杂,是 TCP 协议的精髓所在。这里先给出连续 ARQ 协议最基本的概念,但不涉及许多细节问题。
下图(a)表示发送方维持的发送窗口,它的意义是:位于发送窗口内的 5 个分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。
在讨论滑动窗口时,我们应当注意到,图中还有一个时间坐标(但以后往往省略这样的时间坐标)。按照习惯,“向前”是指“向着时间增大的方向“,而“向后”则是“向着时间减少的方向“。分组发送是按照分组序号从小到大发送的。
连续 ARQ 协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。上图(b)表示发送方收到了对第1 个分组的确认,于是把发送窗口向前移动一个分组的位置。如果原来已经发送了前 5 个分组,那么现在就可以发送窗口内的第 6 个分组了。
接收方一般都是采用累积确认的方式。这就是说,接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,这就表示:到这个分组为止的所有分组都已正确收到了。
累积确认有优点也有缺点。优点是:容易实现,即使确认丢失也不必重传。但缺点是不能向发送方反映出接收方已经正确收到的所有分组的信息。
例如,如果发送方发送了前 5 个分组,而中间的第 3 个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。这就叫做 Go-back-N(回退 N),表示需要再退回来重传已发送过的 N 个分组。可见当通信线路质量不好时,连续 ARQ 协议会带来负面的影响。
TCP 使用的是 GBN 还是选择重传?
这是一个有必要弄清的问题。前面讲过, TCP 使用累积确认,这看起来像是 GBN 的风格。但是,正确收到但失序的报文并不会丢弃,而是缓存起来,并且发送冗余 ACK 指明期望收到的下一个报文段,这是 TCP 方式和 GBN 的显著区别。例如, A 发送了 N 个报文段,其中第 k(k < N)个报文段丢失,其余 N - 1 个报文段正确地按序到达接收方 B 。使用 GBN 时, A 需要重传分组 k,及所有后继分组 k + 1, k + 2, … , N 。相反, TCP 却至多重传一个报文段,即报文段 k 。另外, TCP 中提供一个 SACK(Selective ACK)选项,即选择确认选项。使用选择确认选项时,TCP 看起来就和 SR 非常相似。因此, TCP 的差错恢复机制可视为 GBN 和 SR 协议的混合体。
二、TCP 可靠传输的实现(★★)
TCP 在不可靠的 IP 层之上建立一种可靠数据传输服务。TCP 提供的可靠数据传输服务保证接收方从缓存区读出的字节流与发送方发出的字节流完全一样。TCP 使用了检验、序号、确认和重传等机制来达到这一目的。其中,TCP 的检验机制与 UDP 一样。
1. 以字节为单位的滑动窗口
1)A 的发送窗口
TCP 的滑动窗口是以字节为单位的。如下图所示,现假定 A 收到了 B 发来的确认报文段,其中窗口是 20 字节,而确认号是 31(这表明 B 期望收到的下一个序号是 31,而序号 30 为止的数据已经收到了)。根据这两个数据, A 就构造出自己的发送窗口。
① 我们先讨论发送方 A 的发送窗口(即 31 ~ 50 的区间)。发送窗口表示:在没有收到 B 的确认的情况下, A 可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。
发送窗口里面的序号表示允许发送的序号。显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。之前我们已经讲过,接收方会把自己的接收窗口数值放在窗口字段中发送给对方。因此, A 的发送窗口一定不能超过 B 的接收窗口数值。发送方的发送窗口大小还要受到当时网络拥塞程度的制约。但在目前,我们暂不考虑网络拥塞的影响。
② 发送窗口后沿的后面部分(即 31 之前的区间)表示已发送且已收到了确认,这些数据显然不需要再保留了。而发送窗口前沿的前面部分(即 50 之后的区间)表示不允许发送的,因为接收方都没有为这部分数据保留临时存放的缓存空间。
发送窗口的位置由窗口前沿和后沿的位置共同确定。
-
发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销掉已收到的确认。
-
发送窗口前沿通常是不断向前移动,但也有可能不动。不动对应两种情况:一是没有收到新的确认,对方通知的窗口大小也不变;二是收到了新的确认但对方通知的窗口缩小了,使得发送窗口前沿正好不动。
发送窗口前沿也有可能向后收缩。这发生在对方通知的窗口缩小了。但 TCP 的标准强烈不赞成这样做。因为很可能发送方在收到这个通知以前已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,这样就会产生一些错误。
现在假定 A 发送了序号为 31 ~ 41 的数据。这时,发送窗口位置并未改变(如下图所示),但发送窗口内靠后面有 11 个字节(黑色小方框表示)表示已发送但未收到确认。而发送窗口内靠前面的 9 个字节(42 ~ 50)是允许发送但尚未发送的。
从以上所述可以看出,要描述一个发送窗口的状态需要三个指针:P1, P2 和 P3 (如上图所示)。指针都指向字节的序号,这三个指针指向的几个部分的意义如下:
- 小于 P1 的是已发送并已收到确认的部分,而大于 P3 的是不允许发送的部分。
- P3 - P1 等于 A 的发送窗口。
- P2 - P1 等于已发送但尚未收到确认的字节数。
- P3 - P2 等于允许发送但当前尚未发送的字节数(又称为可用窗口或有效窗口)。
2)B 的接收窗口
再看一下 B 的接收窗口。B 的接收窗口大小是 20 。在接收窗口外面,到 30 号为止的数据是已经发送过确认,并且已经交付主机了。因此在 B 可以不再保留这些数据。接收窗口内的序号(31 ~ 50)是允许接收的。在上图中, B 收到了序号为 32 和 33 的数据。这些数据没有按序到达,因为序号为 31 的数据没有收到(也许丢失了,也许滞留在网络中的某处)。请注意, B 只能对按序收到的数据中的最高序号给出确认,因此 B 发送的确认报文段中的确认号仍然是 31(即期望收到的序号),而不能是 32 或 33 。
现在假定 B 收到了序号为 31 的数据,并把序号为 31 ~ 33 的数据交付主机,然后 B 删除这些数据。接着把接收窗口向前移动 3 个序号(如下图所示),同时给 A 发送确认,其中窗口值仍为 20,但确认号是 34 。这表明 B 已经收到了到序号 33 为止的数据。我们注意到, B 还收到了序号为 37, 38 和 40 的数据,但这些都没有按序到达,只能先暂存在接收窗口中。A 收到 B 的确认后,就可以把发送窗口向前滑动 3 个序号,但指针 P2 不动。可以看出,现在 A 的可用窗口增大了,可发送的序号范围是 42 ~ 53 。
A 在继续发送完序号 42 ~ 53 的数据后,指针 P2 向前移动和 P3 重合。发送窗口内的序号都已用完,但还没有再收到确认(如下图所示)。由于 A 的发送窗口已满,可用窗口已减小到零,因此必须停止发送。
请注意,存在下面这种可能性,就是发送窗口内所有的数据都已正确到达 B ,B 也早已发出了确认。但不幸的是,所有这些确认都滞留在网络中。在没有收到 B 的确认时, A 不能猜测:“或许 B 收到了吧!”为了保证可靠传输, A 只能认为 B 还没有收到这些数据。于是, A 在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,直到收到 B 的确认为止。如果 A 收到确认号落在发送窗口内,那么 A 就可以使发送窗口继续向前滑动,并发送新的数据。
3)TCP 的缓存与窗口的关系
我们知道:发送方的应用进程把字节流写入 TCP 的发送缓存,接收方的应用进程从 TCP 的接收缓存中读取字节流。下面我们就进一步讨论前面讲的窗口和缓存的关系。下图画出了发送方维持的发送缓存和发送窗口,以及接收方维持的接收缓存和接收窗口。这里首先要明确两点:
-
第一,缓存空间和序号空间都是有限的,并且都是循环使用的。最好是把它们画成圆环状的。但这里为了画图的方便,我们还是把它们画成长条状的。
-
第二,由于实际上缓存或窗口中的字节数是非常之大的,因此上图仅仅是个示意图,没有标出具体的数值。但用这样的图来说明缓存和发送窗口以及接收窗口的关系是很清楚的。
① 我们先看一下上图(a)所示的发送方的情况。发送缓存用来暂时存放:
- 发送应用程序传送给发送方 TCP 准备发送的数据;
- TCP 已发送出但尚未收到确认的数据。
发送窗口通常只是发送缓存的一部分。已被确认的数据应当从发送缓存中删除,因此发送缓存和发送窗口的后沿是重合的。发送应用程序最后写入发送缓存的字节减去最后被确认的字节,就是还保留在发送缓存中的被写入的字节数。发送应用程序必须控制写入缓存的速率,不能太快,否则发送缓存就会没有存放数据的空间。
② 再看一下上图(b)所示的接收方的情况。接收缓存用来暂时存放:
- 按序到达的、但尚未被接收应用程序读取的数据;
- 未按序到达的数据。
如果收到的分组被检测出有差错,则要丢弃。如果接收应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到零。反之,如果接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,但最大不能超过接收缓存的大小。上图(b)中还指出了下一个期望收到的字节号。这个字节号也就是接收方给发送方的报文段的首部中的确认号。
根据以上所讨论的,我们还要再强调以下三点:
-
第一,虽然 A 的发送窗口是根据 B 的接收窗口设置的,但在同一时刻, A 的发送窗口并不总是和 B 的接收窗口一样大。这是因为通过网络传送窗口值需要经历一定的时间滞后(这个时间还是不确定的)。另外,发送方 A 还可能根据网络当时的拥塞情况适当减小自己的发送窗口数值。
-
第二,对于不按序到达的数据应如何处理, TCP 标准并无明确规定。如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利(因为发送方会重复传送较多的数据)。因此 TCP 通常对不按序到达的数据是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。
-
第三, TCP 要求接收方必须有累积确认的功能,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。但请注意两点。一是接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络的资源。TCP 标准规定,确认推迟的时间不应超过 0.5 秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认。二是捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。
最后再强调一下, TCP 的通信是全双工通信。通信中的每一方都在发送和接收报文段。因此,每一方都有自己的发送窗口和接收窗口。在谈到这些窗口时,一定要弄清是哪一方的窗口。
2. 超时重传时间的选择
有两种事件会导致 TCP 对报文段进行重传:超时和冗余 ACK 。
TCP 每发送一个报文段,就对这个报文段设置一个超时计时器,TCP 的发送方在计时器设置的重传时间到期但还未收到确认时,就要重传这一报文段。这种重传的概念是很简单的,但重传时间的选择却是 TCP 最复杂的问题之一。
由于 TCP 的下层是互联网环境,IP 数据报所选择的路由变化很大,发送的报文段可能只经过一个高速率的局域网,也可能经过多个低速率的网络,并且每个 IP 数据报所选择的路由还可能不同,所以传输层的往返时延的方差也很大。如果把超时重传时间设置得太短,就会引起很多报文段的不必要的重传,使网络负荷增大。但若把超时重传时间设置得过长,则又使网络的空闲时间增大,降低了传输效率。
1)加权平均往返时间 RTTS
那么,运输层的超时计时器的超时重传时间究竟应设置为多大呢?TCP 采用了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间 RTT(Round-Trip Time)。TCP 保留了 RTT 的一个加权平均往返时间 RTTS(这又称为平滑的往返时间,S 表示 Smoothed,因为进行的是加权平均,因此得出的结果更加平滑),它会随新测量 RTT 样本值的变化而变化。每当第一次测量到 RTT 样本时,RTTS 值就取为所测量到的 RTT 样本值。但以后每测量到一个新的 RTT 样本,就按下式重新计算一次 RTTS :
新的 RTTS = (1 - α) × (旧的 RTTS) + α × (新的 RTT 样本)
在上式中, 0 ≤ α < 1 。若 α 很接近于零,表示新的 RTTS 值和旧的 RTTS 值相比变化不大,而对新的 RTT 样本影响不大(RTT 值更新较慢);若选择 α 接近于 1 ,则表示新的 RTTS 值受新的 RTT 样本的影响较大(RTT 值更新较快)。
已成为建议标准的 RFC 6298 推荐的 α 值为 1/8 ,即 0.125 。用这种方法得出的加权平均往返时间 RTTS 就比测量出的 RTT 值更加平滑。
【思考】:是否 TCP 和 UDP 都需要计算往返时间 RTT?
【答】:往返时间 RTT 仅对传输层 TCP 协议才很重要,因为 TCP 要根据 RTT 的值来设置超时计时器的超时时间。UDP 没有确认和重传机制,因此 RTT 对 UDP 没有什么意义。
因此,不能笼统地说“往返时间 RTT 对传输层来说很重要”,因为只有 TCP 才需要计算 RTT ,而 UDP 不需要计算 RTT 。
2)超时重传时间 RTO
显然,超时计时器设置的超时重传时间 RTO(RetransmissionTime-Out)应略大于上面得出的加权平均往返时间 RTTS ,但也不能大太多,否则当报文段丢失时,TCP 不能很快重传,导致数据传输时延大。RFC 6298 建议使用下式计算 RTO:
RTO = RTTS + 4 × RTTD
而 RTTD 是 RTT 的偏差的加权平均值,它与 RTTS 和新的 RTT 样本之差有关。RFC 6298 建议这样计算 RTTD 。当第一次测量时,RTTD 值取为测量到的 RTT 样本值的一半。在以后的测量中,则使用下式计算加权平均的 RTTD :
新的 RTTD = (1 - β) × (旧的 RTTD)+ β × |RTTS - 新的 RTT 样本|
这里 β 是个小于 1 的系数,它的推荐值是 1/4 ,即 0.25 。
3)Kam 算法(拓展)
上面所说的往返时间的测量,实现起来相当复杂,试看下面的例子。如下图所示,发送出一个报文段,设定的重传时间到了,还没有收到确认。于是重传报文段。经过了一段时间后,收到了确认报文段。
现在的问题是:如何判定此确认报文段是对先发送的报文段的确认,还是对后来重传的报文段的确认?由于重传的报文段和原来的报文段完全一样,因此源主机在收到确认后,就无法做出正确的判断,而正确的判断对确定加权平均 RTTS 的值关系很大。
-
若收到的确认是对重传报文段的确认,但却被源主机当成是对原来的报文段的确认,则这样计算出的 RTTS 和超时重传时间 RTO 就会偏大。若后面再发送的报文段又是经过重传后才收到确认报文段,则按此方法得出的超时重传时间 RTO 就越来越长。
-
同样,若收到的确认是对原来的报文段的确认,但被当成是对重传报文段的确认,则由此计算出的 RTTS 和 RTO 都会偏小。这就必然导致报文段过多地重传。这样就有可能使 RTO 越来越短。
根据以上所述, Kam 提出了一个算法:在计算加权平均 RTTS 时,只要报文段重传了,就不采用其往返时间样本。这样得出的加权平均 RTTS 和 RTO 就较准确。
但是,这又引起新的问题。设想出现这样的情况:报文段的时延突然增大了很多,因此在原来得出的重传时间内,不会收到确认报文段,于是就重传报文段。但根据 Kam 算法,不考虑重传的报文段的往返时间样本。这样,超时重传时间就无法更新。
因此要对 Kam 算法进行修正。方法是:报文段每重传一次,就把超时重传时间 RTO 增大一些。典型的做法是取新的重传时间为旧的重传时间的 2 倍。当不再发生报文段的重传时,才根据上面给出的求 RTO 的式子计算超时重传时间。实践证明,这种策略较为合理。
总之, Kam 算法能够使运输层区分开有效的和无效的往返时间样本,从而改进了往返时间的估测,使计算结果更加合理。
3. 冗余 ACK 进行重传
超时触发重传存在的一个问题是超时周期往往太长。所幸的是,发送方通常可在超时事件发生之前通过注意所谓的冗余 ACK 来较好地检测丢包情况。
冗余 ACK 就是再次确认某个报文段的 ACK ,而发送方先前已经收到过该报文段的确认。例如,发送方 A 发送了序号为 1、2、3、4、5 的 TCP 报文段,其中 2 号报文段在链路中丢失,它无法到达接收方 B 。因此 3、4、5 号报文段对于 B 来说就成了失序报文段。TCP 规定每当比期望序号大的失序报文段到达时,就发送一个冗余 ACK 指明下一个期待字节的序号。在本例中,3、4、5 号报文段到达 B,但它们不是 B 所期望收到的下一个报文段,于是 B 就发送 3 个对 1 号报文段的冗余 ACK ,表示自己期望接收 2 号报文段。
TCP 规定当发送方收到对同一个报文段的 3 个冗余 ACK 时,就可以认为跟在这个被确认报文段之后的报文段已经丢失。就前面的例子而言,当 A 收到对于 1 号报文段的 3 个冗余 ACK 时,它可以认为 2 号报文段已经丢失,这时发送方 A 可以立即对 2 号报文段执行重传,这种技术通常称为快速重传。当然,冗余 ACK 还被用在拥塞控制中。
4. 选择确认 SACK(拓展)
现在还有一个问题没有讨论。这就是若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?答案是可以的。选择确认就是一种可行的处理方法。
我们用一个例子来说明选择确认(Selective ACK)的工作原理。TCP 的接收方在接收对方发送过来的数据字节流的序号不连续,结果就形成了一些不连续的字节块(如下图所示)。可以看出,序号 1 ~ 1000 收到了,但序号 1001 ~ 1500 没有收到。接下来的字节流 1501 ~ 3000 又收到了,可是又缺少了 3001 ~ 3500 。接下来的字节流 3501 ~ 4500 又收到了,再后面从序号 4501 起又没有收到。也就是说,接收方收到了和前面的字节流不连续的两个字节块。如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。
从上图可看出,和前后字节不连续的每一个字节块都有两个边界:左边界和右边界。因此在图中用四个指针标记这些边界。请注意,第一个字节块的左边界 L1 = 1501 ,但右边界 R1 = 3001 而不是 3000 。这就是说,左边界指出字节块的第一个字节的序号,但右边界减 1 才是字节块中的最后一个序号。同理,第二个字节块的左边界 L2 = 3501 ,而右边界 R2 = 4501 。
我们知道, TCP 的首部没有哪个字段能够提供上述这些字节块的边界信息。RFC 2018 规定,如果要使用选择确认 SACK ,那么在建立 TCP 连接时,就要在 TCP 首部的选项中加上“允许SACK”的选项,而双方必须都事先商定好。如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在 TCP 报文段的首部中都增加了 SACK 选项,以便报告收到的不连续的字节块的边界。由于首部选项的长度最多只有 40 字节,而指明一个边界就要用掉 4 字节(因为序号有 32 位,需要使用 4 个字节表示),因此在选项中最多只能指明 4 个字节块的边界信息。这是因为 4 个字节块共有 8 个边界,因而需要用 32 个字节来描述。另外还需要两个字节。一个字节用来指明是 SACK 选项,另一个字节是指明这个选项要占用多少字节。如果要报告五个字节块的边界信息,那么至少需要 42 个字节。这就超过了选项长度的 40 字节的上限。
然而, SACK 文档并没有指明发送方应当怎样响应 SACK 。因此大多数的实现还是重传所有未被确认的数据块。
三、例题
① TCP 协议中滑动窗口的作用是( A )。
A. 流量控制
B. 拥塞控制
C. 路由控制
D. 差错控制
【TCP 采用大小可变的滑动窗口进行流量控制。】
拥塞控制和流量控制的区别在于:流量控制是为了保护接收方,而拥塞控制是为了保护网络。滑动窗口是一种实现流量控制的方法,而不是拥塞控制的方法。虽然在拥塞控制的机制中用到了滑动窗口,但这并不是滑动窗口的作用。
② TCP 的滑动窗口协议中,规定重传分组的数量最多可以( D )。
A. 是任意的
B. 1 个
C. 大于滑动窗口的大小
D. 等于滑动窗口的大小
③ TCP 中滑动窗口的值设置太大,对主机的影响是( A )。
A. 由于传送的数据过多而使路由器变得拥挤,主机可能丢失分组
B. 产生过多的 ACK
C. 由于接收的数据多,而使主机的工作速度加快
D. 由于接收的数据多,而使主机的工作速度变慢
TCP 使用滑动窗口机制来进行流量控制,其窗口尺寸的设置很重要,若滑动窗口值设置得太小,则会产生过多的 ACK(因为窗口大可以累积确认,因此会有更少的 ACK);若设置得太大,则又会由于传送的数据过多而使路由器变得拥挤,导致主机可能丢失分组。
④ 一个 TCP 连接的数据传输阶段,若发送端的发送窗口值由 2000 变为 3000 ,则意味着发送端可以( C )。
A. 在收到一个确认之前可以发送 3000 个 TCP 报文段
B. 在收到一个确认之前可以发送 1000B
C. 在收到一个确认之前可以发送 3000B
D. 在收到一个确认之前可以发送 2000 个 TCP 报文段
TCP 提供的是可靠的字节流传输服务,使用窗口机制进行流量控制与拥塞控制。TCP 的滑动窗口机制是面向字节的,因此窗口大小的单位为字节。假设发送窗口的大小为 N ,这意味着发送端可以在没有收到确认的情况下连续发送 N 个字节。