对 TCP 可靠传输的一些思考

TCP 是面向连接的、基于字节流的「可靠」传输协议;相较于 UDP ,TCP 依靠序号、ACK应答机制、重传机制、流量控制(滑动窗口)、拥塞控制算法能够在错综复杂的网络中确保消息的可靠传输~

1. 前言

在上大学《计算机网络》这门课的时候,老师也会给我们讲 TCP 、讲 UDP ,只不过当时老师讲的更多的是什么是 TCP、什么是 UDP,它是如何工作的,但是没有给我们讲为什么要有这个东西,有这个东西能做什么?再后来接触学习 TCP 协议是大学毕业要准备找工作了,这个时候就看网上文章的各种分析,因为缺少一些实习经验,还是没有真正理解其在我们实际应用中真正的作用;

今年是工作的第三年,在过去的工作经验中,频繁的接触 HTTP 协议、RPC 框架,网络通信相关「充满」在日常工作中,最近重新梳理网络相关知识,过去似懂非懂的知识,这个时候又有了更清晰的认识,所谓“读万卷书,不如行万里路”,实践过,经历过,才能让我们对事物的认识更加具象化,所以如果当前时间段比较迷茫,不妨先绕过,等以后再回头看,有可能会有其他收获~

2. TCP 是什么

传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议;—— wiki 百科

「面向连接」、「可靠」、「基于字节流」三个特性,TCP 为了实现这三个特性引入了序号、ACK 应答机制、重传机制,流量控制(滑动窗口)机制;

3. 序号机制

TCP 工作在传输层,数据在 TCP层 被称为流(相应的数据在 IP 中称为包、在 MAC 中称为帧),TCP 承接来自应用层需要传输的数据,负责将其「运送」到目标机器,应用层的数据有可能很小、也有可能很大,TCP 每次能够传输的数据有限制;即 MSS (Maximum Segement Size );

对于超过最大限制的数据会将按照 MSS 将其进行分为多个报文进行传输;TCP 为了提高发送速率,引入了「滑动窗口」,实际传输中,多个报文有可能在网络中同时进行传输,因为网络传输的不确定性,无法确认对方先接收到哪一个报文;最终对方接收到多个报文,也不知道正确的顺序是怎么样的;

所以 TCP 为每一个报文都分配了一个序号,例如将数据 Data 拆分成两个报文:报文1、报文2,即使对方先接收到报文 2 也知道还有一个报文 1,等报文 1 也顺序到达之后,再根据序号组装成正确的数据「上交」给应用层;从而保证了消息的顺序性与正确性。

alt text

4. ACK 应答机制

TCP 通过 ACK 应答机制来反馈发送方,发送的数据已经成功被对方接收到。

alt text

在TCP协议中,ACK(确认)字段中的序号表示的是下一个期望接收的数据字节的序号;发送方的报文1 成功被接收方接收到之后,接收方会返回下一个序号的 ACK,表示你的报文 1 我已经收到了,接下来我希望收到序号为 2 的数据报文

5. 重传机制

在上面实例中,有可能会因为网络原因出现以下两种异常场景:

  1. 发送方在发送数据的时候有可能会因为网络丢失
    alt text
  2. 接收方收到数据之后返回 ACK 给到发送方的时候丢失
    alt text

上述两种情况,对于发送方来说,都无法正常收到 ACK 报文,也就意味着数据并没有发送成功;TCP 针对上述这种情况,有对应的重传机制:超时重传、快速重传;

5.1 超时重传

Retransmission Timeout 超时重传:指当一个数据段被发送出去后,发送方等待接收方确认的时间长度。如果在这个时间内没有收到确认,发送方将认为该数据段丢失,并重新发送。RTO的数值是基于平滑的往返时间(RTT)及其偏差来计算的。

很好理解,在发出报文之后,在超过 RTO 之后没有接收到 ACK 报文,则会对数据报文进行重传;关键在于这个「时间」的大小:

  1. 时间间隔太大导致网络吞吐降低;
  2. 时间间隔太小有可能会导致原本正常网络上传输的报文被「误认为」超时了

在 TCP 中,这个 RTO 是实时变化的,会比「报文往返时间」大一点;如下所示

alt text

优点:
实现简单,从数据报文发出开始计时,在收到相应 ACK 报文之后,其间相差的时间就是「报文往返时间」;超时时间设置的比「报文往返时间」大一点即可。

缺点:
在发生因网络原因超时的原因时,会一直等到超过了 RTO 也没有返回 ACK 报文才触发重传;期间发送方啥也没做,吞吐变低了。

5.2 快速重传

针对上述 RTO 不容易设置的刚刚好的场景,TCP 引入了一个快速重传的机制;如下所示

A. 数据报文在网络中丢失

alt text

  1. 发送方发送的数据报文 1 在网络传输时丢失;
  2. 因为「滑动窗口」的存在,发送方继续发送数据报文 2、3、4;
  3. 接受方在成功收到数据报文 2、3、4 之后则都返回 ACK 1;
  4. 此时发送方连续收到三个 ACK 为 1 的报文,表示没有收到数据包 1 ,此时发送方就会重传数据报文 1。

发送方连续接收到对同一个序号的 ACK 报文,即便在没有超过 RTO 的情况下,也会对丢失的数据报文进行重传。

B. ACK 报文丢失

alt text

  1. 发送方发送的数据报文 1 成功被接收方所接收,但 ACK 报文在网络传输过程中丢失了;
  2. 如果后续发送方还有继续发送数据报文,并且能成功收到 ACK 的话,则同样也能够感知到自己发送的数据报文 1 被成功接收;
  3. 例如收到了 ACK 3 则表示数据报文 3 之前的数据都成功的被接收了

这种机制称为「累计应答」。

C. 快速重传的缺点

快速重传很好的解决了:如果数据报文在网络传输过程中丢失了,需要一直等待超过 RTO 才进行重传的缺点,接收方通过快速返回同一序号的 ACK 报文 3 次,让发送方感知到相应的数据报文丢失了;

但是同样也带来缺点:

1. 不必要的重传

假设在一个网络中,由于路由器缓冲区的短暂拥塞导致部分数据包延迟到达接收端。如果接收端已经收到了后续的数据包并发送了三个重复的ACK给发送端,发送端可能会错误地认为中间的数据包丢失,并触发快速重传。然而,实际上被认为“丢失”的数据包随后到达了接收端。

alt text

2. 无法明确重传哪些数据报文

假设发送方需要发送总共 5 个数据报文,数据报文 1、2 在网络中丢失了,数据报文 3、4、5 顺利到达,按照「快速重传」的约定,会在收到数据报文 3、4、5 的时候返回 ACK 1 给到发送方,发送方触发重传数据报文 1,接收方收到后返回 ACK 2,即丢失的数据报文 2 ,但是这个时候只有一个 ACK 2,无法触发「快速重传」机制;此时 只能等待 在超过 RTO 之后,发送方会重传没有收到 ACK 的数据报文 2。

发送方可以选择在收到连续 3 个 ACK 1 之后,将数据报文 2、3、4、5 重新发送,但这样产生了很多重复的发送。

D. SACK

Selective Acknowledgment,即选择性确认;针对「快速重传」无法明确需要重传哪些数据报文的情况,SACK 能够在返回 ACK 的时候通知发送方已经成功收到哪些数据报文段了。
alt text

SACK 搭配「快速重传」机制能够保证重传效率的同时,减少重复报文的传输。

6. 滑动窗口

滑动窗口机制是为了提升 TCP 发送和接收的效率,如果每一个数据报文都需要等待成功接收到 ACK 报文之后再进行发送的话,对于发送方来说,有较多空闲时间(在等待 ACK 报文)

alt text

对于发送方来说,在发送数据报文 1 之后,需要等待 ACK 报文回来在进行发送;然后才发送数据报文 2;

滑动窗口则是在发送方和接收方都维护一个「窗」,大小由双方协定「拥塞控制的关键」,如下所示:

alt text

  1. 窗口大小为 4,发送方在发送数据报文的时候,不用等待 ACK 报文的返回就可以将后续「还未发送」的数据报文发送出去
  2. 对于发送方来说,发送数据报文 1 之后,不用等待 ACK 2 的接收,就可以将数据报文 2、3、4 发送出去,提高了吞吐量;
  3. 对于接收方来说,如果同时接收到数据报文 1、2、3、4,并不需要应答 ACK 2、3、4、5,而是只需要应答 ACK 5,发送方接收到 ACK 5 之后就知道前面序号的数据报文已经成功被接收;即「累计应答
  4. 发送方发现「窗口」内的报文都成功发送出去了,就会继续选中后续 4 个数据报文,整体看起来类似「滑动」,所以称为「滑动窗口」。

滑动窗口结合了序号、ACK应答、重传等机制,保证报文可靠性~

7. 拥塞控制

TCP 还能根据传输的网络质量自行调节发送速率,避免在网络繁忙的时候大量传输报文,导致网络拥堵,容易造成超时,然后触发重试,导致网络更加拥;也会在网络不繁忙的时候,尽可能的加快发送速率。

实现速率控制就是依赖调整「滑动窗口」的大小,窗口怎么调,什么时候调多大,TCP 引入了拥塞控制算法:

  1. 慢启动
  2. 拥塞避免
  3. 快重传
  4. 快恢复

这里就不一一展开来讲,因为我也没记住 >_< hhh

言而总之,总而言之,拥塞控制算法就是通过控制窗口的大小来控制 TCP 的传输速率,在没有出现超时的情况情况,就持续地调大窗口大小,「慢启动和拥塞避免算法就是在控制初期窗口的应该调多大」;当出现连续收到三个相同序号的 ACK 报文时,即发生了「快重传」,则说明网络有可能出现了拥堵,需要慢点了,需要将窗口大小调小,但是也不能调太小,所以「快恢复」算法就是来计算当出现「快重传」的时候,窗口大小应该调整到多大;

8. 总结

TCP 作为传输层协议,承接应用层数据,其「保证」将数据成功发送到接收方,主要是依靠以上序号、ACK应答、重传机制、滑动窗口、拥塞控制机制来实现对消息的「可靠」传输;当然 TCP 并没有这里列觉的那么简单,还有很多异常场景、小的场景可以拿出来讲,在后面,分多篇文章来写一写理解和总结~

留下一个问题:UDP 同样作为传输层的协议,其是可靠的吗?为什么?有什么优缺点吗?