运输层——TCP的可靠传输

可靠传输的工作原理

IP 网络所提供的是不可靠的传输。因此,TCP 必须采用适当的措施才能使得两个运输层之间的通信变得可靠。

理想的传输条件有以下两个特点:

  • 传输信道不产生差错。
  • 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。

在这样的理想传输条件下,不需要采取任何措施就能够实现可靠传输。

然而实际的网络都不具备以上两个理想条件。必须使用一些可靠传输协议,在不可靠的传输信道实现可靠传输。

停止等待协议

“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。

全双工通信的双方既是发送方也是接收方。为了方便,仅考虑 A 发送数据,而 B 接收数据并发送确认。因此 A 叫做发送方,而 B 叫做接收方。

1.无差错情况

A 发送分组 M1,发完就暂停发送,等待 B 的确认。B 收到了 M1 向 A 发送 ACK。A 在收到了对 M1 的确认后,就再发送下一个分组 M2

2.出现差错

在接收方 B 会出现两种情况:

  • B 接收 M1 时检测出了差错,就丢弃 M1,其他什么也不做(不通知 A 收到有差错的分组)。
  • M1 在传输过程中丢失了,这时 B 当然什么都不知道,也什么都不做。

在这两种情况下,B 都不会发送任何信息。但 A 都必须重发分组,直到 B 正确接收为止,这样才能实现可靠通信。

问题:A如何知道 B 是否正确收到了 M1 呢?

解决方法:超时重传

  • A 为每一个已发送的分组都设置了一个超时计时器。
  • A 只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器,继续发送下一个分组 M2
  • 若A在超时计时器规定时间内没有收到B的确认,就认为分组错误或丢失,就重发该分组。

问题:若分组正确到达B,但B回送的确认丢失或延迟了,A未收到B的确认,会超时重发。B 可能会收到重复的 M1。B如何知道收到了重复的分组,需要丢弃呢?

解决方法:编号

  • A为每一个发送的分组都进行编号。若B收到了编号相同的分组,则认为收到了重复分组,丢弃重复的分组,并回送确认。
  • B为发送的确认也进行编号,指示该确认是对哪一个分组的确认。
  • A根据确认及其编号,可以确定它是对哪一个分组的确认,避免重发发送。若为重复的确认,则将其丢弃。

3. 确认丢失和确认迟到

确认丢失

若 B 所发送的对 M1 的确认丢失了,那么 A 在设定的超时重传时间内不能收到确认,但 A 并无法知道:是自己发送的分组出错、丢失了,或者是 B 发送的确认丢失了。因此 A 在超时计时器到期后就要重传 M1

假定 B 又收到了重传的分组 M1。这时 B 应采取两个行动:

  • 第一,丢弃这个重复的分组 M1,不向上层交付。
  • 第二,向 A 发送确认。不能认为已经发送过确认就不再发送,因为 A 之所以重传 M1 就表示 A 没有收到对 M1 的确认。

确认迟到

传输过程中没有出现差错,但 B 对分组 M1 的确认迟到了。

A 会收到重复的确认。对重复的确认的处理很简单:收下后就丢弃。

B 仍然会收到重复的 M1,并且同样要丢弃重复的 M1,并重传确认分组。

请注意

在发送完一个分组后,必须暂时保留已发送的分组的副本,以备重发。

分组和确认分组都必须进行编号。

超时计时器的重传时间应当比数据在分组传输的平均往返时间更长一些。

通常 A 最终总是可以收到对所有发出的分组的确认。如果 A 不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信。

使用上述的确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信。

像上述的这种可靠传输协议常称为自动重传请求 ARQ(Automatic Repeat reQuest)。意思是重传的请求是自动进行的,接收方不需要请求发送方重传某个出错的分组。

4. 信道利用率

停止等待协议的优点是简单,缺点是信道利用率太低。

假定 A 发送分组需要的时间是 TD。TD 等于分组长度除以数据率。分组正确到达 B 后,B 处理分组的时间忽略不计,同时立即发送确认。假定 B 发送确认分组需要时间 TA。如果 A 处理确认分组的时间忽略不计,那么 A 在经过时间 TD + RTT + TA 后就可以再发送下一个分组,这里的 RTT 是往返时间。

可以看出,当往返时间 RTT 远大于分组发送时间 TD 时,信道的利用率就会非常低。

若出现重传,则对传送有用的数据信息来说,信道的利用率就还要降低。

为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。

流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认。这样可使信道上一直有数据不间断地传送。

由于信道上一直有数据不间断地传送,这种传输方式可获得很高的信道利用率。

停止等待协议要点:

  • 停止等待。发送方每次只发送一个分组。在收到确认后再发送下一个分组。
  • 编号。对发送的每个分组和确认都进行编号。
  • 自动重传请求。发送方为每个发送的分组设置一个超时计时器。若超时计时器超时,发送方会自动重传分组。
  • 简单,但信道利用率太低。

连续 ARQ 协议

基本思想:

  • 发送方一次可以发出多个分组。
  • 使用滑动窗口协议控制发送方和接收方所能发送和接收的分组的数量和编号。
  • 每收到一个确认,发送方就把发送窗口向前滑动一个分组的位置。
  • 接收方一般采用累积确认的方式。
  • 采用回退N(Go-Back-N)方法进行重传。

发送方维持的发送窗口,它的意义是:位于发送窗口内的分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。

滑动窗口协议




累积确认

接收方一般采用累积确认的方式。即不必对收到的分组逐个发送确认,而是对按序到达的最后一个分组发送确认,这样就表示:到这个分组为止的所有分组都已正确收到了。

优点:容易实现,即使确认丢失也不必重传。

缺点:不能向发送方反映出接收方已经正确收到的所有分组的信息。

Go-back-N(回退 N)

如果发送方发送了前 5 个分组,而中间的第 3 个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。这就叫做 Go-back-N(回退 N),表示需要再退回来重传已发送过的 N 个分组。

可见当通信线路质量不好时,连续 ARQ 协议会带来负面的影响。

连续 ARQ 协议与停止等待协议

连续ARQ协议 停止等待协议
发送的分组数量 一次发送多个分组 一次发送一个分组
传输控制 滑动窗口协议 停等-等待
确认 单独确认 + 累积确认 单独确认
超时定时器 每个发送的分组 每个发送的分组
编号 每个发送的分组 每个发送的分组
重传 回退N,多个分组 一个分组

TCP 可靠传输的实现

为了方便,我们假定数据传输只在一个方向进行,即 A 发送数据,B 给出确认。这样的好处是使讨论限于两个窗口,即发送方 A 的发送窗口和接收方 B 的接收窗口。

以字节为单位的滑动窗口

TCP 使用流水线传输和滑动窗口协议实现高效、可靠的传输。

TCP 的滑动窗口是以字节为单位的。

发送方 A 和接收方 B 分别维持一个发送窗口和一个接收窗口。

根据 B 给出的窗口值,A 构造出自己的发送窗口。假定 A 收到了 B 发来的确认报文段,其中窗口是 20 字节,而确认号是 31(这表明B期望收到的下一个序号是 31,而序号 30 为止的数据已经收到了)。根据这两个数据,A 就构造出自己的发送窗口。

先看发送方 A 的发送窗口。发送窗口表示:在没有收到确认的情况下,可以连续把窗口内的数据全部发送出去。

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

上图中,B 收到了序号为 32 和 33 的数据。这些数据没有按序到达,因为序号 31 的数据没有收到(也许丢失,也许滞留)。因此 B 发送的确认报文段中的确认号仍为 31。

现在假定 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 向前移动和 P 3 重合,发送窗口内的序号都已用完,但还没有再收到确认。由于 A 的发送窗口已满,可用窗口已减小到 0,因此必须停止发送。

如果 A 收到确认号落在发送窗口内,那么 A 就可以使发送窗口继续向前滑动,并发送新的数据。

发送缓存与接收缓存的作用

发送缓存用来暂时存放:

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

接收缓存用来暂时存放:

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

需要强调三点:

  1. A 的发送窗口并不总是和 B 的接收窗口一样大(因为有一定的时间滞后)。另外,发送方 A 还可能根据网络当时的拥塞情况适当减小自己的发送窗口数值。
  2. TCP 标准没有规定对不按序到达的数据应如何处理。通常是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。
  3. TCP 要求接收方必须有累积确认的功能,这样可以减小传输开销。

接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。

但请注意两点:

  1. 接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络的资源。TCP 标准规定,确认推迟的时间不应超过 0.5 秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认。
  2. 捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。

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

超时重传时间的选择

重传机制是 TCP 中最重要和最复杂的问题之一。

TCP 每发送一个报文段,就对这个报文段设置一次计时器。只要计时器设置的重传时间到但还没有收到确认,就要重传这一报文段。

由于 TCP 的下层是一个互联网环境,IP 数据报所选择的路由变化很大。因而运输层的往返时间(RTT)的方差也很大。

TCP 超时重传时间设置

如果把超时重传时间设置得太短,就会引起很多报文段的不必要的重传,使网络负荷增大。

但若把超时重传时间设置得过长,则又使网络的空闲时间增大,降低了传输效率。

TCP 采用了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间 RTT。

加权平均往返时间

TCP保留了RTT的一个加权平均往返时间RTTS(这又称为平滑的往返时间)。

第一次测量到 RTT 样本时,RTTS 值就取为所测量到的 RTT 样本值。以后每测量到一个新的 RTT 样本,就按下式重新计算一次 RTTS

新的RTTS = (1 - α) * (旧的RTTS) + α * (新的RTT样本)

式中,0 ≤ α < 1。若α很接近于零,表示 RTT 值更新较慢。若选择α接近于 1,则表示 RTT 值更新较快。

RFC 6298 推荐的 α 值为 1/8,即 0.125。

超时重传时间 RTO

RTO(Retransmission Time-Out) 应略大于上面得出的加权平均往返时间 RTTS

RFC 6298 建议使用下式计算 RTO:

RTO = RTTS + 4 * RTTD

RTTD 是 RTT 的偏差的加权平均值。

RFC 6298 建议这样计算RTTD。第一次测量时, RTTD 值取为测量到的 RTT 样本值的一半。在以后的测量中,则使用下式计算加权平均的 RTTD

新的 RTTD = (1 - β) * (旧的RTTD) + β | RTTS - 新的 RTT 样本 |

β是个小于 1 的系数,其推荐值是 1/4,即 0.25。

往返时间 (RTT) 的测量相当复杂

TCP 报文段 1 没有收到确认。重传(即报文段 2)后,收到了确认报文段 ACK。

如何判定此确认报文段是对原来的报文段 1 的确认,还是对重传的报文段 2 的确认?

Karn 算法

在计算平均往返时间 RTT 时,只要报文段重传了,就不采用其往返时间样本。

这样得出的加权平均平均往返时间 RTTS 和超时重传时间 RTO 就较准确。

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

修正的 Karn 算法

报文段每重传一次,就把 RTO 增大一些:

新的 RTO = γ * (旧的 RTO)

系数γ的典型值是 2。

当不再发生报文段的重传时,才根据报文段的往返时延更新平均往返时延 RTT 和超时重传时间 RTO 的数值。

实践证明,这种策略较为合理。

选择确认 SACK

问题:若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?

答案是可以的。选择确认 SACK(Selective ACK) 就是一种可行的处理方法。

接收到的字节流序号不连续

TCP 的接收方在接收对方发送过来的数据字节流的序号不连续,结果就形成了一些不连续的字节块。

和前后字节不连续的每一个字节块都有两个边界:边界和右边界。

第一个字节块的左边界L1 = 1501,但右边界R1 = 3001。左边界指出字节块的第一个字节的序号,但右边界减 1 才是字节块中的最后一个序号。

第二个字节块的左边界L2 = 3501,而右边界R2 = 4501

RFC 2018 的规定

如果要使用选择确认,那么在建立 TCP 连接时,就要在 TCP 首部的选项中加上“允许 SACK”的选项,而双方必须都事先商定好。

如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在 TCP 报文段的首部中都增加了 SACK 选项,以便报告收到的不连续的字节块的边界。

由于首部选项的长度最多只有 40 字节,而指明一个边界就要用掉 4 字节,因此在选项中最多只能指明 4 个字节块的边界信息。

打赏
  • Copyrights © 2017-2023 WSQ
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信