可视化 TCP 重传 !终于有人将 TCP 传输讲明白了

由于 IP 层和 MAC 层对数据包的长度有严格限制,TCP 在将数据交给 IP 层之前,必须先将过大的数据拆分成多个大小合适的段(Segment),确保每个段的长度都在允许的范围之内。

下面这张图简单说明了这些数据段是如何在互联网上传输的。

如上图所示,客户端的 HTTP 层准备向目标服务器发送一段 18 字节的字节流。

在第 16–18 字节还没到达 TCP 层时,前面的 15 个字节已经传递到了 TCP 层。TCP 会将这些数据打包成一个或多个段(Segment),并在每个段前添加 TCP 头部(图中数据包的黄色部分)。

随后,这些 TCP 段在 IP 层再次封装附加上 IP 头部(数据包的橘黄色区域),接着经过网络传输,最终到达服务器端。

假设一个 TCP 段的长度超过了 IP 层所支持的最大长度,IP 层会将这个数据包进一步拆分为更小的片段。但这个“分片”过程非常消耗资源,因此我们通常希望尽量避免 IP 层参与分片。

那么,TCP 是如何确定每个段的长度,从而避免 IP 层再次分片的呢?这就依赖于 MSS(Maximum Segment Size,最大报文段长度) 的设置。

理想情况下,我们希望选取一个合适的 MSS,使得:

在现代网络环境中,MSS 的值是 1460 字节,这个数值是怎么得出的呢?

因此,MSS = 1500 - 20 - 20 =1460。

可以看出,MSS 表示的是 TCP 报文段中“纯数据”部分的最大长度,不包括任何头部信息。

MSS 是在 TCP 三次握手过程中协商确定的。如下图所示,MSS 位于 TCP 首部的选项字段中。

在第一次握手时,客户端会在发送的 SYN 报文中声明自己建议使用的 MSS 值,例如 1460 字节,这被称为发送方最大报文段长度(SMSS)。

随后,服务器在响应的 SYN-ACK 报文中也会声明自己的 MSS 值,比如 1400 字节。由于这个值表示服务器所能接收的最大报文段长度,因此被称为接收方最大报文段长度(RMSS)。

TCP 重传

为了确保数据能够可靠传输,TCP 实现了两个关键机制:

下面我们通过一个简单的模型来说明这个过程。

  1. 一开始,发送方在发送数据后会启动一个定时器。
  2. 发送方在定时器超时之前收到了接收方返回的第一个 ACK,说明数据成功送达,此时定时器会被重置,并用于接下来的数据发送。这个过程不断重复。
  3. 当第二个数据包在发送后,由于定时器设置的超时时间到达了还未收到对应的 ACK,说明这个数据可能丢失了,于是会重新发送这条数据。

这种设计虽然简单直观,但效率较低,因为发送方必须等待每一条消息的 ACK 返回后,才能继续发送下一条消息,

为了解决这个问题,我们可以对这个机制进行优化。

通过为每条消息分配一个编号,就可以实现同时发送多条消息,而无需等待前一条消息的确认。每个编号对应一条独立的数据消息,同时关联自己的定时器和 ACK。

如果某条消息(比如第 3 条)在传输过程中丢失,发送方就只需重新发送那一条丢失的消息,而不是重传整个消息序列。

那么,在 TCP 中这个“消息编号”是什么呢?其实就是序列号。

以下是一个消息示例。

TCP 的序列号是一个 32 位的无符号整数,因此它的取值范围为 0 到 2³² - 1,超过这个范围后会重新从 0 开始。

不过,这种“序列号回绕”的机制也可能带来问题。

为了便于讲解,假设序列号最大只能到 4,并且每次只发送 1 个字节,在这个过程中,第 2 个字节(序号为 #2)丢失了,按照机制,它会稍后被重新发送;发送方继续发送更多字节,直到序列号从 #4 回绕回到 #0,再到 #1;就在这个时候,之前丢失的 #2 被重新发送了。

此时问题出现了:接收方无法判断这个 #2 是原来那条丢失的数据,还是新一轮发送的第 2 字节。这就可能导致数据被错误接收或重复接收,从而引发混乱。

这种问题也叫序列号的歧义问题,为了解决它,TCP 在选项中引入了时间戳(timestamp)。

时间戳的主要作用是:帮助接收方识别具有相同序列号但属于不同传输周期的数据段。

我们可以为每个数据段附加一个时间戳。当接收方收到某个数据段后,会读取其时间戳(记为 B),并与上一个正常接收到的数据包其时间戳(记为 E)进行比较。如果 B 小于等于 E,就能确认这是一次重传的旧数据。否则,如果是正常的新数据,时间戳应该比 E 更大。

通过这个机制,即使两个数据段的序列号相同,接收方也能通过时间戳准确判断它们的“新旧”,从而避免因序列号回绕造成的混淆。

TCP快速重传

TCP 提供了一种称为 快速重传(Fast Retransmit) 的机制,能够在无需等待定时器超时的情况下,尽早发现并重传丢失的数据段。

为了实现快速重传,发送方和接收方需要遵循一套明确的规则。

规则 1:接收方的行为

接收方在收到一个数据段后,应始终通过确认号(ACK)字段返回它期望接收的下一个序列号。

比如,接收方收到序号为 1 的数据段后,会回复 ACK 2,表示它希望接下来收到的是序号为 2 的数据段。

如果接收方之后收到了序号为 3、4 的数据段,而 序号为 2 的数据段还未收到,那么它依然会反复发送 ACK 2,告诉发送方:“我还在等 #2。”

规则 2:发送方的行为

作为发送方,当连续收到 3 个重复的 ACK 时,就可以判断:这个数据段已经丢失了。

此时,发送方不再等待定时器超时,而是立即重传丢失的数据段,提高了恢复丢包的速度。

下面的图展示了快速重传的一个例子:

图中,前两个数据段成功发送并被接收方确认,然而,第 3 个数据段在传输过程中丢失了。

接下来,接收方依次收到了第 4、5、6 个数据段。根据 TCP 协议的规则,由于第 3 个数据段尚未收到,接收方始终期望它的到来,因此每次都返回 ACK 3。

此时,发送方检测到已连续收到了 三个相同的 ACK,根据快速重传机制,立即将丢失的第 3 个数据段重新发送出去,无需等待重传定时器超时。

当接收方成功接收到重传的第 3 个数据段后,序列号恢复连续,于是发送 ACK 7,表示希望接收到第 7 个数据段。通信重新回到正常节奏。

不过,这里还存在一个潜在问题:在快速重传第 3 个数据段之前,发送方其实并不确定第 5 和第 6 个数据段是否已经成功被接收。如果这两个数据段同样丢失了,发送方并不会立刻察觉,仍需等待超时后再进行重传,这将延长数据恢复的时间。

为了优化这一过程,TCP 引入了 SACK(Selective Acknowledgment,选择性确认) 机制。

TCP 选择性确认

有了 SACK,接收方可以在确认报文中额外告知发送方哪些数据段已经成功收到,这样,发送方就能准确知道哪些数据已经到达、哪些还需重传,从而避免重复发送和不必要的等待,大大提升了传输效率。

TCP 的选择性确认(SACK)是通过 TCP 选项字段实现的。

在 SACK 中,我们可以明确指出除了 ACK 确认号之外,哪些额外的数据区间已经成功接收。

比如下面这个例子中,SACK 告诉发送方:从 2872 到 3393 的数据已经接收到了。

借助这个信息,发送方就知道这段数据不需要重传。

同时,发送方也可以据此判断出哪些数据段尚未到达(可能已经丢失),从而快速、有针对性地发起重传,提高整体的传输效率。

总结

展开阅读全文

更新时间:2026-10-19

标签:数码   数据   字节   定时器   序列号   长度   报文   机制   消息   快速   时间

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight All Rights Reserved.
Powered By 61893.com 闽ICP备11008920号
闽公网安备35020302034844号

Top