技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTNET

计算机网络:TCP 如何保证传输可靠性

所属馆藏
NET Protocol
文件格式
Markdown
原始路径
NET Protocol/30-计算机网络-TCP如何保证传输可靠性
本文目录9 个章节
  1. 1. 校验和
  2. 2. 序列号与确认应答
  3. 3. 超时重传与快速重传
  4. 4. 连接管理
  5. 5. 流量控制
  6. 6. 拥塞控制
  7. 7. TCP 可靠传输机制总结
  8. 延伸阅读
  9. 参考资料

计算机网络:TCP 如何保证传输可靠性

Category(分类): NET Protocol Status: 未知

原文参考:TCP协议如何保证传输可靠性

TCP 协议的主要特点是面向连接、面向字节流,并且为应用程序提供可靠、有序的传输服务。IP 层本身只负责尽力而为地传递数据,可能出现丢包、重复、乱序和比特错误等情况,因此 TCP 在 IP 之上增加了一系列机制来提高传输的可靠性。

TCP 保证可靠传输的主要机制包括:

  1. 校验和;
  2. 序列号与确认应答;
  3. 超时重传和快速重传;
  4. 连接管理;
  5. 流量控制;
  6. 拥塞控制。

其中,校验和主要用于发现传输过程中的数据损坏;序列号和 ACK 用于识别数据、处理乱序和重复;重传机制用于恢复丢失或未被确认的数据;流量控制用于保护接收方,拥塞控制用于保护网络。

1. 校验和

校验和用于检查报文在传输过程中是否发生了比特错误。发送方根据报文内容计算校验和值并填入报文,接收方收到报文后使用相同的算法重新计算并进行比较。

如果校验结果不符合要求,接收方通常会丢弃该报文。TCP 不需要单独发送“校验失败”的通知,发送方如果没有收到有效 ACK,之后可能通过超时重传或快速重传恢复数据。

需要注意,IPv4 首部校验和与 TCP 校验和不是同一个概念:

  • IPv4 首部校验和只检查 IPv4 首部,不检查 TCP 首部和 TCP 数据;IPv6 没有 IPv4 这种首部校验和。
  • TCP 校验和是 TCP 可靠传输的一部分,覆盖 TCP 首部、TCP 数据以及由 IP 地址、协议号和 TCP 长度组成的 IP 伪首部。IPv4 和 IPv6 的 TCP 报文都需要进行 TCP 校验和校验。

校验和能够发现大量传输错误,但它不是密码学完整性校验。由于校验和存在碰撞的可能,即使校验和相同,也不能从数学上保证数据绝对没有发生错误。

IPv4 首部校验和示例

下面保留一个 IPv4 首部校验和的计算示例。这个示例只用于说明 IPv4 首部校验和的算法,不能当作 TCP 数据校验和的计算示例。

IPv4 首部校验和结构示意图

计算 IPv4 首部校验和时,需要:

  1. 将校验和字段(16 位首部校验和)置为 0;
  2. 按 16 bit 为单位对 IPv4 首部进行反码加法求和;
  3. 将产生的进位加回低 16 位,重复进行,直到没有高 16 位进位;
  4. 将得到的 16 位结果按位取反,填入校验和字段。

示例 IPv4 首部如下:

45 00    00 31
89 F5    00 00
6E 06    00 00(校验和字段,计算时置为 0)
DE B7    45 5D       -> 222.183.69.93
C0 A8    00 DC       -> 192.168.0.220

计算过程:

4500 + 0031 + 89F5 + 0000 + 6E06 + 0000
+ DEB7 + 455D + C0A8 + 00DC
= 0x322C4

0x0003 + 0x22C4 = 0x22C7
~0x22C7 = 0xDD38

因此,该 IPv4 首部的校验和可以填为 DD38。接收方把 DD38 也加入计算:

4500 + 0031 + 89F5 + 0000 + 6E06 + DD38
+ DEB7 + 455D + C0A8 + 00DC
= 0x3FFFC

0x0003 + 0xFFFC = 0xFFFF
~0xFFFF = 0x0000

计算结果为 0,说明该首部通过了这次校验。

2. 序列号与确认应答

TCP 提供的是有序字节流,而不是带有消息边界的报文消息。应用程序写入 TCP 的数据会被 TCP 拆分成合适大小的多个 TCP 报文段,接收方再按照序列号重新排序,并将连续的字节流交给应用程序。

数据在经过路由器、网关等设备时,可能发生丢失、重复或乱序,因此 TCP 需要使用序列号和确认应答来识别数据。

2.1 MSS 与 TCP 分段

MSSMaximum Segment Size 的缩写,表示单个 TCP 报文段允许携带的最大 TCP 数据长度,不包括 IP 首部和 TCP 首部。它不是“最大消息长度”,也不是整个 IP 数据包的最大长度。

在 IPv4、MTU 为 1500 字节、IP 首部和 TCP 首部都没有额外选项时,MSS 常见值为:

1500 - 20(IPv4 首部)- 20(TCP 首部)= 1460 字节

但 1460 不是固定值。IPv6、隧道、VPN、TCP 选项或其他网络环境都可能导致 MSS 不同。

MSS 选项只出现在 SYN 报文中,包括初始 SYN 和 SYN+ACK。它表达的是发送该选项的一方愿意接收的单个 TCP 报文段数据大小。双方的 MSS 可以不同,发送方应根据对端通告的 MSS 限制自己发送的 TCP 数据段大小,而不是简单地认为双方永远使用同一个较小值。

TCP 根据 MSS 把连续字节流拆分为多个 TCP 报文段,这个过程称为 TCP 分段,不应与 IP 分片混淆。现代网络通常会通过 MSS、路径 MTU 发现等方式尽量避免 IP 分片。

MSS 与 TCP 分段示意图

2.2 序列号

TCP 会对字节流中的每个字节进行编号。一个 TCP 数据段的序列号通常表示该段中第一个数据字节的序号。例如:

第一个数据段:序列号 1,携带字节 1~1000
第二个数据段:序列号 1001,携带字节 1001~2000
第三个数据段:序列号 2001,携带字节 2001~3000

TCP 序列号是 32 位,并且会按模 2^32 回绕。SYN 和 FIN 虽然不携带普通应用数据,但各自都会占用一个序列号;纯 ACK 不占用序列号空间。

序列号的作用包括:

  • 按正确顺序重新组装数据;
  • 识别重复数据并丢弃重复部分;
  • 判断哪些数据已经确认,哪些数据仍然需要重传;
  • 配合接收窗口限制可接受的数据范围。

2.3 确认应答

TCP ACK 通常是累计确认。ACK 号表示接收方期望收到的下一个序列号,也就是说,小于该 ACK 号的数据已经连续收到。

例如,接收方连续收到序列号为 1~1000 的数据后,可以回复:

ACK = 1001

这表示序列号小于 1001 的数据已经连续收到,接下来希望收到序列号 1001 开始的数据。

TCP 不要求每收到一个报文段就立即发送一个 ACK。接收方可能采用延迟确认,也可能把 ACK 捎带在自己发送的数据中;收到乱序数据时,还可能发送重复 ACK,提示发送方某个序列号之前的数据仍然缺失。因此,TCP 不是“发送一个数据包、等待一个 ACK、再发送下一个”的停等协议,而是可以利用滑动窗口连续发送多个未确认报文段。

TCP 序列号、确认应答与滑动窗口示意图

3. 超时重传与快速重传

TCP 发送数据后,会记录尚未确认的数据。如果数据段或 ACK 在网络中丢失,发送方可能迟迟收不到确认。TCP 使用重传机制恢复这些数据。

3.1 超时重传

发送方会为未确认的数据维护重传计时器。如果计时器超时,发送方通常会重传最早尚未确认的数据段。

TCP 的重传超时时间(RTO)不是固定值,而是根据往返时间 RTT 和 RTT 的波动情况动态估算。常见计算会结合平滑往返时间 SRTT、往返时间方差 RTTVAR 和时钟粒度 G,形式类似:

RTO = SRTT + max(G, 4 × RTTVAR)

实际实现还会设置最小值、最大值和初始值。根据 RFC 6298,在还没有 RTT 样本时,初始 RTO 通常取 1 秒,但具体实现可能有所调整。

超时后,RTO 通常会进行指数退避,例如将等待时间加倍;重传次数达到系统配置的上限后,操作系统可能认为连接不可用并向应用程序报告错误。具体重传次数、计时粒度和连接清理策略由操作系统及配置决定,不能统一写成 500ms 的整数倍。

如果 ACK 丢失但数据实际上已经到达,发送方也可能重传相同的数据。接收方可以根据序列号识别重复数据并重新发送 ACK,因此重传不会导致应用程序收到重复的字节流。

3.2 快速重传

如果接收方收到后续数据,却发现中间某段缺失,通常会重复确认当前连续数据末尾的下一个序列号。发送方连续收到 3 个重复 ACK 时,经典 TCP Reno 通常会认为存在报文段丢失,在重传计时器超时前立即重传可能丢失的报文段,这就是快速重传。

快速重传并不一定能证明发生了丢包,严重的网络乱序也可能产生重复 ACK。现代 TCP 还可能使用 SACK 等选项更准确地描述已经收到的非连续数据。

4. 连接管理

TCP 的连接管理包括连接建立、数据传输状态维护和连接关闭。三次握手用于同步双方的初始序列号、确认连接两端已经准备好通信,并避免历史重复连接请求造成混乱;四次挥手用于分别关闭两个方向的数据传输。

连接管理本身不是“每个数据都可靠”的直接原因,但它为序列号、确认号、窗口和重传机制建立了必要的状态基础。

正常关闭时使用 FIN,异常终止时可能使用 RST。主动关闭的一方通常需要经历 TIME-WAIT,以便重新发送最后的 ACK,并避免旧连接的延迟报文干扰后续连接。

5. 流量控制

流量控制用于防止发送方发送速度过快,导致接收方的接收缓冲区被填满。

接收方会在 TCP 报文的窗口字段中通告自己当前还能接收多少数据,这个值通常称为接收窗口 rwnd。发送方根据对端通告的接收窗口限制尚未确认的数据量,从而避免接收缓冲区溢出。

5.1 接收窗口与窗口扩大

TCP 首部中的窗口字段本身是 16 位,未使用窗口扩大选项时最多表示 65535 字节。为了支持高带宽、高延迟网络,双方可以在 SYN 阶段协商 Window Scale 选项。

窗口扩大因子是一个移位值,实际接收窗口可以理解为:

实际接收窗口 = TCP 窗口字段值 << 窗口扩大因子

窗口扩大因子最大为 14。它不是“TCP 首部剩余 40 字节中的一个普通窗口字段”,而是 TCP 选项空间中的独立选项,并且必须在连接建立时协商;如果没有协商成功,就不能使用窗口扩大。

接收窗口表示接收方愿意接收的数据量,MSS 表示单个 TCP 段的最大数据长度,二者含义不同。

5.2 滑动窗口和零窗口

滑动窗口不是单纯“接收端使用的窗口”,而是 TCP 发送方和接收方共同参与的机制:

  • 接收方通过 rwnd 告知发送方自己还能接收多少数据;
  • 发送方维护已发送但未确认的数据范围,并在窗口允许的范围内连续发送;
  • 当接收方应用程序读取数据后,缓冲区腾出空间,接收方会在后续 ACK 中通告更大的窗口。

发送方实际允许发送的数据量通常受以下两个窗口中较小者限制:

发送窗口 ≈ min(rwnd, cwnd)

其中 rwnd 是接收窗口,cwnd 是拥塞窗口。

如果接收方通告窗口为 0,发送方通常会停止发送新的应用数据,但仍需要通过持久计时器发送零窗口探测,以确认对方窗口是否已经重新打开。接收方窗口恢复后,发送方才能继续发送数据。

6. 拥塞控制

流量控制解决的是“接收方是否来得及处理”的问题,拥塞控制解决的是“网络是否承受得住”的问题。

通信双方通常无法直接知道网络中所有路由器和链路的剩余容量,因此 TCP 发送方需要根据 ACK、丢包、RTT 等反馈估计网络状况,并维护拥塞窗口 cwnd。拥塞窗口限制的是发送方在网络中可以保持的未确认数据量。

经典 TCP 拥塞控制通常包括:

  • 慢启动(slow start);
  • 拥塞避免(congestion avoidance);
  • 快速重传(fast retransmit);
  • 快速恢复(fast recovery)。

不同 TCP 实现和算法的具体行为可能不同。例如 Linux 常见实现使用 CUBIC,BBR 等算法也不完全遵循经典 Reno 的窗口变化公式。下面的快速恢复主要描述经典 Reno 的典型流程。

6.1 慢启动

当 TCP 刚开始发送数据时,并不清楚当前网络的可用容量。如果一开始就发送大量数据,可能立即造成拥塞,因此发送方通常从较小的拥塞窗口开始,并根据收到的 ACK 逐渐增大 cwnd

现代 TCP 的初始拥塞窗口不一定是 1 个 MSS,通常可以是多个 MSS,具体取值取决于规范和实现。为了便于理解,如果使用早期教材中的简化模型,假设初始 cwnd = 1 MSS,并且每个报文段都及时得到确认,那么窗口可能近似表现为:

开始:1 MSS
经过约 1 个 RTT:2 MSS
经过约 2 个 RTT:4 MSS
经过约 3 个 RTT:8 MSS

这种“每个 RTT 近似翻倍”的现象来自慢启动阶段每收到一个有效 ACK,窗口最多增加约一个 SMSS。实际增长会受到延迟 ACK、ACK 丢失、初始窗口、实现策略和拥塞阈值 ssthresh 的影响。

慢启动中的“慢”不是指窗口增长得很慢,而是指开始发送时先使用较小窗口进行探测。达到 ssthresh 或检测到拥塞后,TCP 通常会转入拥塞避免或执行丢包恢复流程。

RTT(Round-Trip Time)表示往返时延,即报文从发送方到接收方再返回确认所经历的时间。一个传输轮次还强调了发送窗口允许的数据被连续发送,并等待相应确认的过程,二者并不完全等价。

6.2 拥塞避免

在拥塞避免阶段,拥塞窗口增长速度会比慢启动慢,经典 Reno 通常近似每经过一个 RTT 增加一个 SMSS,使 cwnd 按线性规律增长,而不是继续指数增长。

“拥塞避免”并不代表可以完全避免拥塞,而是通过降低增长速度,使网络不容易迅速进入拥塞状态。

当发送方通过重传超时判断发生严重拥塞时,经典 TCP 通常会执行类似以下操作:

ssthresh = max(FlightSize / 2, 2 × SMSS)
cwnd     = 较小的初始值

然后重新进入慢启动。这里的 FlightSize 是已经发送但尚未被累计确认的数据量,不一定等于 cwnd。现代实现可能根据具体拥塞控制算法采用不同策略。

不能简单地把“没有马上收到 ACK”都视为拥塞。ACK 可能延迟或丢失,TCP 通常要结合重复 ACK、重传超时和 RTT 等多种信息进行判断。

6.3 快速重传

经典 TCP Reno 在收到 3 个重复 ACK 后,会认为某个报文段可能丢失,并在重传定时器超时前立即重传该报文段。

重复 ACK 通常包含相同的确认号,表示接收方仍在等待某个序列号开始的数据。例如,接收方已经收到后续报文段,但缺少序列号为 1001 的数据,它可能多次回复:

ACK = 1001

快速重传可以比等待 RTO 更快地恢复丢失数据,但也可能被网络乱序误触发。SACK 可以帮助发送方了解接收方已经收到哪些非连续数据。

6.4 快速恢复

在经典 Reno 中,收到第 3 个重复 ACK 后通常会:

  1. 根据当前 FlightSize 计算新的 ssthresh,通常约为其一半,但不能低于 2 × SMSS
  2. 重传推测丢失的报文段;
  3. 临时将 cwnd 设置为 ssthresh + 3 × SMSS,因为 3 个重复 ACK 表明后续报文段仍有可能已经到达接收方;
  4. 每收到一个额外的重复 ACK,临时将 cwnd 增加约一个 SMSS,并在窗口允许时发送新的报文段;
  5. 收到确认新数据的 ACK 后,将 cwnd 调整为 ssthresh,进入拥塞避免阶段。

这是一种经典 Reno 的描述。现代 TCP 的拥塞控制、SACK 恢复以及 CUBIC、BBR 等算法可能采用不同的窗口调整方式,不能将上述步骤当作所有 TCP 实现的固定行为。

7. TCP 可靠传输机制总结

机制主要解决的问题
TCP 校验和发现 TCP 首部或数据在传输中发生的比特错误
序列号标识字节流位置,处理乱序和重复数据
累计 ACK告诉发送方哪些连续数据已经收到,以及下一个期望的序列号
超时重传在 ACK 长时间未到达时恢复可能丢失的数据
快速重传根据重复 ACK 提前恢复可能丢失的数据,减少等待 RTO 的时间
连接管理同步初始序列号,维护连接状态,并有序关闭连接
流量控制防止发送方耗尽接收方缓冲区
拥塞控制防止发送速率超过网络承载能力

这些机制共同作用,使 TCP 在不可靠的 IP 网络之上,为应用程序提供可靠、有序、无重复的字节流。TCP 并不能保证网络永远不丢包,也不能保证数据一定在某个时间内送达;它的可靠性是通过检测问题、确认数据和重新传输等方式实现的。

延伸阅读

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS