TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。
1 可靠传输的工作原理
使用可靠传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当降低发送数据的速度。
自动重传请求(Automatic Repeat-reQuest,ARQ)是 OSI 模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认帧,它通常会重新发送。ARQ 包括停止等待 ARQ 协议和连续 ARQ 协议。
1.1 停止等待 ARQ 协议
“停止等待"就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
- 优点:简单
- 缺点:信道利用率低,等待时间长
1)确认再发送,超时重传
- 确认再发送:发送方发送分组,接收方在规定时间内收到,并且回复确认。发送方再次发送。
- 超时重传:
- 要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。
- 因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。
2)确认丢失和确认迟到
- 确认丢失
- 确认消息在传输过程丢失,发送方超时重传了
- 若接收方收到重复分组,就丢弃该分组,但同时还要发送确认。
- 确认迟到
- 确认丢失之后,若发送方又收到重复确认消息
- 表明之前的确认不是丢失,是迟到,直接丢弃
1.2 连续 ARQ 协议(流水线)
为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认。这样可使信道上一直有数据不间断地在传送。
连续 ARQ 协议可提高信道利用率,采用流水线传输。
- 发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。
- 接收方一般采用“累积确认”,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
- 发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。
- 优点:信道利用率高,容易实现,即使确认丢失,也不必重传。
- 缺点:不能向发送方反映出接收方已经正确收到的所有分组的信息。
- 比如:发送方发送了 5条 消息,中间第三条丢失(3号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。
- 这也叫 Go-Back-N,表示需要退回来重传已经发送过的 N 个消息。
2 可靠传输的实现
- TCP 连接的每一端都必须设有两个窗口 —— 一个发送窗口和一个接收窗口。
- TCP 的可靠传输机制用字节的序号进行控制。TCP 所有的确认都是基于序号而不是基于报文段。
- TCP 两端的四个窗口经常处于动态变化之中。
- TCP 连接的往返时间 RTT 也不是固定不变的。
2.1 滑动窗口
窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。
发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。
接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。
发送缓存和接收缓存
- 发送缓存用来暂时存放:
- 发送应用程序传送给发送方 TCP 准备发送的数据;
- TCP 已发送出但尚未收到确认的数据。
- 接收缓存用来暂时存放:
- 按序到达的、但尚未被接收应用程序读取的数据;
- 不按序到达的数据。
2.2 超时重传时间的选择
TCP 采用了一种自适应算法,记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间 RTT。 TCP 保留了 RTT 的一个加权平均往返时间 RTTs。
在计算加权平均 RTTs 时,只要报文段重传了,就不采用其往返时间样本。报文段每重传一次,就把超时重传时间 RTO 增大一些。
2.3 选择确认 SACK
选择确认(Selective ACK),可让发送方只传送缺少的数据而不重传己经正确到达接收方的数据。
要在 TCP 首部的选项中加上“允许 SACK”的选项,而双方必须都事先商定好。在 TCP 报文段的首部中都增加了 SACK 选项,以便报告收到的不连续的字节块的边界。
SACK 文档并没有指明发送方应当怎样响应 SACK。因此大多数的实现还是重传所有未被确认的数据块。