为什么说 TCP 协议是可靠的?

几张图带你彻底了解 TCP 协议。

TCP 协议是 TCP/IP 协议栈中的传输层的协议,TCP协议又叫传输控制协议(Transport Control Protocal)。

众所周知,它是一个可靠协议。因为它能保证接收端完整地接受到发送端发送的数据包,即保证不丢包。

那 TCP 协议如何保证不丢包呢?

这个是本文重点讲述的内容。

1.TCP 协议的作用

ISO(国际标准化组织)曾提出一个 OSI 七层模型。

将网络的协议划分为 7 个层,从低到高排序是:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

但是这个模型仅停留在理论阶段。

因为该模型过于庞大、复杂,以至于无法被广泛应用。

后来技术人员在 TCP/IP 等协议集问世之后,提出 TCP/IP 协议栈。该模型很贴近实际场景,所以被广泛的应用。

TCP/IP 协议栈一共分为 4 个层次。

从低到高依次排序是:数据链路层(有书籍称之为网络接口层)、网络层、传输层、应用层。

TCP/IP 协议栈

  • 网络接口层:针对不同物理网络的连接形式的协议:以太网、FDDI 光纤分布式数据接口。其中协议有 ARP 协议(地址解析协议)、RARP 协议(反向地址转换协议)
  • 网际层:负责数据的传输,路由以及地址选择。最主要的协议是 IP 协议。
  • 传输层:确认数据传输以及进行纠错处理。传输层中中有两个非常重要的协议,即 TCP 协议和 UDP 协议。
  • 应用层:各种服务以及应用程序。常见的应用层协议有 HTTP 协议、FTP 协议(文件传输协议)、SMTP 协议(简单邮件传输协议)等

2.TCP 协议通信特点

TCP 协议是实现端口到端口的通信。它虚拟了本文流(byte stream)的通信。

我们知道,计算机数据的本质是有序的 0/1 序列(如果以byte为单位,就叫做文本流)。

计算机的功能就是储存和处理文本流。所以TCP 是采用“流”通信

但是传输层的下一层是网络层。即 TCP 协议的下一层协议是 IP 层。

这就意味着 TCP 协议最终还是由 IP 协议规定的形式传输数据。

IP 协议是以数据包方式传送

同时,IP 数据包的 MTU 也有长度限制。

所以 TCP 协议会将数据切割为一个个片段,然后丢给网络层,接着打包成一个个数据包进行传输

TCP 协议会将数据切割为一个个片段

但是这样,流数据变成了一个个片段数据,这会无法保证数据到达的次序。

因为 IP 协议在传输过程中,不会按顺序进行发送和接受数据包。

针对这问题,TCP 协议为了确保数据到达的顺序与文本流顺序相同。

TCP 协议将每个 TCP 片段中分为**头部(header)和数据(payload)**两部分。

每个头部中带有一个序号

这相当于给每个片段增加一个序号标记,方便后续排序。

这相当于给每个片段增加一个序号标记

3.TCP 实现可靠通信的两种方式

我们都知道 IP 协议是“不太靠谱”。

因为 IP 协议是不可靠的,所以 IP 数据包可能在传输过程中发生错误或者丢失。

这就意味着,TCP 协议不得不面对以下三个问题。

  • 1)每个数据包有可能发送不成功
  • 2)数据包在传输过程中有可能被丢弃
  • 3)接收端有可能接受不到数据包

TCP 为了解决这丢包问题,提出两个补救措施。

ACK 回复

在每收到一个正确的、符合次序的片段之后,就向发送方(也就是连接的另一段)发送一个特殊的 TCP 片段,用来知会(ACK,acknowledge)发送方:我已经收到那个片段了。

这个特殊的 TCP片段 叫做 ACK 回复

如果一个片段序号为 L,对应ACK 回复有回复号 L+1,也就是接收方期待接收的下一个发送片段的序号。

重新发送机制

如果发送方在一定时间等待之后,还是没有收到 ACK 回复,那么它推断之前发送的片段一定发生了异常。

发送方会**重复发送(retransmit)**那个出现异常的片段,等待 ACK 回复,如果还没有收到,那么再重复发送原片段…

直到收到该片段对应的 ACK 回复(回复号为 L+1 的 ACK)。

4.TCP 的滑动窗口

虽然采用 “ACK 回复” + “重新发送机制” 方式能实现不丢包,但是会存在两个问题。

1.效率低的问题

发送方保持发送 -> 等待ACK -> 发送 -> 等待ACK…的单线工作方式,这样的工作方式叫做 stop-and-wait。

stop-and-wait 虽然实现了 TCP 通信的可靠性,但同时牺牲了网络通信的效率。

同时,在等待ACK的时间段内,我们的网络都处于闲置(idle)状态

2.有点小缺陷

如果片段一直没有被确认,会导致后续的片段无法发送出去。

TCP 为了进一步优化解决这两个问题,提出**滑动窗口(sliding window)**的概念。

滑动窗口被同时应用于接收方和发送方, 发送方和接收方各有一个滑窗。

当片段位于滑窗中时,表示 TCP 正在处理该片段。

此外,如果滑窗中可以有多个片段,也就是可以同时处理多个片段。

我们借助一些图片来进一步了解下滑动窗口内部机制。

黄色框框表示可以容纳三个片段的固定大小的滑窗。

在图中,并假设片段从左向右排列。

实际运用中,滑动窗口是可变的,窗口大小是字节(byte)来计算的。

对于发送方来说,滑窗的左侧为已发送并已 ACK 过的片段序列,滑窗右侧是尚未发送的片段序列。

如果滑动窗口第一个片段一直没有收到 ACK 回复,窗口不会向右滑动。

但是发送方还是可以继续发送后面两个片段数据包。

对于接受方来说,滑窗的左侧是已经正确收到并 ACK 回复过的片段,也就是正确接收到的文本流。

滑窗中的片段是期望接收的片段。如果滑窗中第一个片段先收到, 滑窗会向右移动。

如果滑窗中后面两个片段先收到,但是第一个片段没有收到。

窗口不会向右滑动。

滑动窗口-1

发送端已经发送三个数据包(1、2、3),在等待每个数据包的 ACK 回复

滑动窗口-2

接收端成功收到两个数据包,回复两个 ACK。还有一个数据包没有收到。当收到 数据包 1 时,接收端会回复一个 ACK 1,然后将窗口向有滑动一个位置。

滑动窗口-3

发送端成功接收到 ACK 1 回复

滑动窗口-4

发送端的窗口向右滑动一个位置

滑动窗口-5

在没有收到 ACK 2 和 3 的回复,还能继续发送数据包 4

滑动窗口-6

之前数据包 4 已经发送了。在之后成功收到 ACK 2 和 3 的回复,窗口向右滑动两个位置,现在又能继续发送数据包 5,6

通过上面一系列图片,我们可以大致知道滑动窗口的机制。我们来做下小总结:

  • 对于发送端

如果滑动窗口第一个片段一直没有收到 ACK 回复,窗口不会向右滑动。但是发送方还是可以继续发送后面两个片段数据包。

  • 对于接受端

如果滑窗中第一个片段先收到,滑窗会向右移动。如果滑窗中后面两个片段先收到,但是第一个片段没有收到。窗口不会向右滑动

那么实际应用中确实是这样吗?如果接收方每接受一个片段,就回复一个 ACK。这种效率有点低。所以实际应用中, TCP 协议为了减少了 ACK 回复所消耗的流量,采用的是累计 ACK 回复。 接收方往往利用一个 ACK 回复来知会连续多个片段的成功接收。通过累计 ACK,所需要的 ACK 回复通常可以降到 50%。

我们同样通过图片的形式来了解累计 ACK 回复的原理。

累计 ACK 回复的原理

在图中,橙色为已经接收的片段。方框为滑窗,滑窗可容纳3个片段。

情况1:滑窗还没接收到片段 7 时,已接收到片段 8,9。这样就在滑窗中制造了一个“空穴”(hole)。

情况2:当滑窗最终接收到片段7时,滑窗送出一个回复号为 10 的 ACK 回复。发送方收到该回复,会意识到,片段 10 之前的片段已经按照次序被成功接收。

整个过程中节约了片段 7 和片段 8 所需的两个 ACK 回复。

文章参考内容: