【计算机网络 | 传输层3:TCP 协议概述:面向连接、可靠传输到底意味着什么?】

上一篇介绍 UDP 时,我们看到了一种非常克制的传输协议:它无须建立连接,保留应用报文边界,却不负责确认、重传、排序、流量控制和拥塞控制。

如果应用传输的是文件、网页内容、数据库请求或支付信息,情况就不同了。接收方不仅要收到数据,还希望数据保持正确顺序,不能缺失,也不能因为重传而重复交付。

TCP 正是为这类需求设计的。

TCP 的全称是 Transmission Control Protocol,中文通常译为传输控制协议。它在不可靠的 IP 服务之上,通过连接状态、字节编号、确认、重传、窗口等机制,为应用提供可靠的字节流传输。

不过,"面向连接"和"可靠传输"很容易被过度简化。TCP 连接不是一条真实线路,可靠也不表示网络永远不会失败。本篇先建立 TCP 的整体认识,再从报文段结构中观察这些能力是怎样获得支撑的。

一、TCP 要解决什么问题?

IP 提供的是尽力而为交付。一个 IP 数据报在传输过程中可能:

  • 丢失;
  • 重复;
  • 乱序;
  • 因差错而被丢弃;
  • 在网络中经历不可预测的时延。

UDP 基本保留了这种服务特征。TCP 则在 IP 之上增加一套端到端控制机制,希望向应用提供这样的抽象:

发送应用写入一串字节,接收应用最终按照相同顺序读到同一串字节。

为实现这一目标,TCP 需要解决几个问题:

  1. 通信双方如何确认彼此存在并初始化状态?
  2. 怎样判断哪些字节已经发送、收到和确认?
  3. 报文段丢失后怎样发现并重传?
  4. 报文段乱序到达后怎样恢复原始字节顺序?
  5. 接收方处理不过来时怎样限制发送速度?
  6. 网络发生拥塞时怎样减少注入网络的数据?
  7. 连接结束时怎样让双方有序释放状态?

这些问题共同决定了 TCP 比 UDP 复杂得多。

二、TCP 的五个核心特点

1. 面向连接

TCP 是面向连接的传输层协议。应用正式传送数据前,通信双方需要建立 TCP 连接;数据传输完成后,还需要释放连接。

这里的"连接"是一种逻辑连接。它并不表示两台主机之间建立了一条专用物理线路,也不意味着沿途路由器都保存了这条连接。

TCP 连接主要由两个端系统共同维护。建立连接的过程可以帮助双方:

  • 确认对端具备通信能力;
  • 同步初始序号;
  • 协商最大报文段长度、窗口扩大、选择确认等选项;
  • 创建发送缓存、接收缓存和连接控制块;
  • 初始化序号、窗口、计时器等状态变量。

中间的普通路由器通常只看到一个个独立的 IP 数据报,根据 IP 首部进行转发,并不知道两端正在维护怎样的 TCP 状态。

2. 一对一通信

每条 TCP 连接只有两个通信端点,因此 TCP 提供的是一对一通信。

一条已建立的 TCP 连接通常由四元组标识:

text 复制代码
源 IP 地址、源端口、目的 IP 地址、目的端口

同一个服务器端口可以同时服务大量客户端,是因为这些连接的源 IP 或源端口不同,从而形成不同四元组。

TCP 不直接提供一对多广播或多播。如果应用需要同时向多个接收者发送数据,通常要建立多条 TCP 连接,或者选择更适合多播的数据报机制。

3. 可靠交付

TCP 希望在连接正常维持的前提下,把发送方字节流可靠地交给接收应用。这里的可靠主要包括:

  • 检测传输差错;
  • 发现并恢复丢失数据;
  • 消除重复数据;
  • 按原始顺序交付;
  • 不把损坏的数据当作正确数据交给应用。

TCP 为此组合使用校验和、序号、确认号、计时器、重传、接收缓存等机制。

但可靠传输不是"永远传输成功"。如果主机断电、网络长时间中断或连接被复位,TCP 最终可能报告连接失败。可靠的准确含义是:TCP 不会悄悄把缺失、乱序或重复的字节当成完整字节流交给应用;无法继续恢复时,会把错误状态暴露给应用。

4. 全双工通信

TCP 是全双工协议。连接建立后,双方可以同时发送和接收数据。

text 复制代码
主机 A  ─────────>  主机 B
主机 A  <─────────  主机 B

这不是两条彼此独立的 TCP 连接,而是一条连接中的两个数据方向。每个方向都有自己的字节序号和确认过程。

因此,连接两端通常都需要:

  • 发送缓存;
  • 接收缓存;
  • 发送序号状态;
  • 接收序号状态;
  • 与流量控制、重传相关的变量。

一个方向的数据和另一个方向的确认还可以放在同一个报文段中,这称为捎带确认

5. 面向字节流

TCP 面向字节流,而不是面向应用报文。

应用可能分三次写入数据:

text 复制代码
第 1 次写入:ABC
第 2 次写入:DEFG
第 3 次写入:HI

TCP 看到的是一串连续字节:

text 复制代码
ABCDEFGHI

TCP 可以根据发送窗口、最大报文段长度、拥塞状态和实现策略,把这些字节组合成不同大小的报文段。接收应用读取数据时,也不保证一次读取正好对应发送方的一次写入。

例如,接收方可能这样读到:

text 复制代码
第一次读取:ABCDE
第二次读取:FGHI

字节内容和顺序没有变化,但读写边界不再一一对应。

因此,使用 TCP 的应用层协议必须自行定义消息边界,常见方法包括:

  • 固定长度;
  • 特殊分隔符;
  • 在消息首部写入长度;
  • 使用能够自描述边界的编码格式。

这也是 TCP 与 UDP 最直观的区别之一:UDP 保留报文边界,TCP 提供连续字节流。

三、面向字节流不等于逐字节发送

TCP 会为字节流中的字节编号,但不会为了每个字节都单独发送一个报文段。

实际过程通常是:

  1. 应用把数据写入 TCP 发送缓存;
  2. TCP 从缓存中选取一段连续字节;
  3. 在这些字节前添加 TCP 首部,组成 TCP 报文段;
  4. TCP 报文段交给 IP 层封装和发送;
  5. 接收端从报文段中取出数据,按序放入接收缓存;
  6. 接收应用从缓存中读取连续字节流。

TCP 传输的数据单元仍然是报文段

text 复制代码
TCP 报文段 = TCP 首部 + 一段应用字节流

"字节流"描述 TCP 向应用提供的服务形式,"报文段"描述 TCP 在网络中实际发送的数据单元,二者不矛盾。

1. TCP 为什么要调整报文段大小?

如果每个字节都单独发送,首部开销会非常大;如果报文段过大,又可能超过路径能够承载的数据规模,引发 IP 分片或直接无法发送。

TCP 组织报文段时会综合考虑:

  • 已经进入发送缓存的数据量;
  • 对方通告的接收窗口;
  • 当前拥塞窗口;
  • 最大报文段长度 MSS;
  • 路径最大传输单元等下层限制;
  • 具体 TCP 实现的发送策略。

2. MSS 是什么?

MSS 是 Maximum Segment Size,即最大报文段数据长度。它限制的是一个 TCP 报文段中数据部分的最大长度,不包含 TCP 首部。

text 复制代码
TCP 报文段长度 = TCP 首部长度 + TCP 数据长度
TCP 数据长度通常不应超过协商得到的 MSS

MSS 通常在建立连接时协商,其选择会考虑接口或路径的 MTU,以尽量避免 IP 分片。

3. 发送窗口不等于报文段长度

发送窗口表示发送方当前最多可以有多少字节处于允许发送但尚未全部确认的范围;报文段长度则表示一次封装发送了多少字节。

可以把二者理解为:

  • 发送窗口是当前允许在途的数据容量;
  • 报文段是一次实际装载和发送的数据单位。

一个发送窗口通常可以覆盖多个 TCP 报文段,不能把"窗口为 64 KB"理解成"每个报文段就是 64 KB"。

四、TCP 连接由谁维护?

TCP 连接状态主要存在于两个端系统。

1. 发送缓存

发送缓存通常保存:

  • 应用已经写入、尚未发送的数据;
  • 已经发送、但尚未被确认的数据。

已经发送但未确认的数据暂时不能随意丢弃,因为如果 TCP 判断它可能丢失,还需要重新发送。

2. 接收缓存

接收缓存通常保存:

  • 已经按序到达、但应用尚未读取的数据;
  • 已经到达但前面仍有缺口的乱序数据。

当缺失部分补齐后,TCP 才能把连续字节范围继续交给应用。

3. 连接控制状态

两端还要维护:

  • 本方向下一个发送字节的序号;
  • 已确认到什么位置;
  • 期望收到对方哪个字节;
  • 接收窗口和拥塞窗口;
  • 重传计时器;
  • 连接当前所处状态;
  • 双方协商的 TCP 选项。

交换机主要依据 MAC 地址转发帧,普通路由器主要依据 IP 地址转发数据报。它们通常不会替端系统维护上述 TCP 状态。

某些防火墙、NAT 或负载均衡设备会跟踪 TCP 连接,但这是中间设备为了过滤、地址转换或流量调度而增加的功能,不改变 TCP 端到端维护连接的基本设计。

五、TCP 的"可靠"是怎样搭起来的?

TCP 可靠传输不是由某一个字段单独实现的,而是多种机制共同作用的结果。

1. 校验和:发现报文是否损坏

TCP 校验和覆盖 TCP 首部和数据,并结合 IP 伪首部中的源地址、目的地址、协议号等信息进行计算。

校验和能够检测许多比特差错,但它只负责检错,不能自动修复错误。

2. 序号:给字节流标记位置

TCP 会对每个方向上的字节流进行编号。报文段首部中的序号,表示该报文段数据部分第一个字节的编号。

如果一个报文段的序号是 1001,携带 100 字节数据,那么它覆盖的字节范围是:

text 复制代码
1001 ~ 1100

接收方可以利用序号判断报文段的位置、顺序、重复与缺口。

3. 确认号:告诉对方下一个期望字节

TCP 首部中的确认号表示:

接收方下一步期望收到的字节序号。

如果接收方返回确认号 1101,通常表示序号 1100 及之前的连续字节都已经收到,现在期待字节 1101。

这是一种累计确认思想:确认号 N 表示 N 之前的连续字节已经被确认,而不是只确认编号为 N 的某个报文段。

4. 重传:恢复可能丢失的数据

发送方会保留未确认数据。如果等待超过一定时间仍未收到相应确认,或者收到的确认模式表明中间可能存在缺口,TCP 可以重传相关数据。

超时应该设置多长、重复确认如何触发快速重传,以及选择确认如何减少不必要重传,将在后续文章中单独展开。

5. 接收缓存:处理乱序到达

IP 数据报可能经过不同路径,TCP 报文段不一定按发送顺序到达。接收端可以暂存乱序数据,等待缺失字节到达,再按照字节序号向应用交付连续数据。

6. 流量控制:不要压垮接收方

接收方通过窗口字段告诉发送方,自己当前还能接收多少数据。发送方据此限制未确认数据范围,避免接收缓存被过快填满。

7. 拥塞控制:不要压垮网络

即使接收方缓存足够大,中间网络也可能已经拥塞。TCP 还会根据丢包、确认和往返时延等信号调整发送强度,减少对网络的冲击。

流量控制关注接收端能力,拥塞控制关注网络承载状况,两者不能混为一谈。

六、TCP 报文段的整体结构

TCP 的连接管理、可靠传输、流量控制和拥塞控制,都需要报文段首部中的字段提供信息。

一个 TCP 报文段由首部和数据两部分组成:

text 复制代码
+--------------------------------------------------+
|                    TCP 首部                       |
+--------------------------------------------------+
|                应用字节流的一部分                  |
+--------------------------------------------------+

TCP 固定首部为 20 字节。首部还可以携带选项,传统 TCP 首部最大为 60 字节,因此选项与填充最多占 40 字节。

一个简化的经典首部结构如下:

下面抓住最重要的字段理解。

七、TCP 首部中的关键字段

1. 源端口与目的端口

源端口和目的端口各占 16 位,用于标识通信两端的应用套接字。它们与源、目的 IP 地址一起组成 TCP 连接的四元组。

2. 序号

序号字段占 32 位,表示本报文段数据部分第一个字节的序号。

TCP 序号在下面的范围中循环使用:

text 复制代码
0 ~ 2^32 - 1

达到最大值后会按模 2^32 回绕。连接建立时,双方分别选择各自方向的初始序号。

SYN 和 FIN 虽然通常不携带普通应用数据,也会各自消耗一个序号空间位置,这一点在连接建立与释放时非常重要。

3. 确认号

确认号字段占 32 位。ACK 标志有效时,它表示期望从对方收到的下一个字节序号。

例如:

text 复制代码
确认号 = 301

表示接收方已经连续收到 300 号及之前的相关字节,现在期待 301 号字节。

TCP 两个方向的序号空间彼此独立,因此一个报文段可以同时携带本方向的数据序号和对另一个方向数据的确认号。

4. 数据偏移

数据偏移字段指出 TCP 数据部分从哪里开始,本质上表示 TCP 首部长度。

它的单位是 4 字节。字段占 4 位,最大值为 15,因此 TCP 首部最大长度为:

text 复制代码
15 × 4 字节 = 60 字节

最小首部为 20 字节,对应数据偏移值 5。

之所以需要这个字段,是因为 TCP 选项长度可变。接收方必须先知道首部有多长,才能找到数据起始位置。

5. 标志位

经典 TCP 首部中最常见的六个控制标志如下:

标志 主要作用
URG 表示紧急指针有效
ACK 表示确认号字段有效
PSH 提示接收端尽快把数据交给应用
RST 复位或拒绝异常连接
SYN 建立连接时同步序号
FIN 表示本方向不再发送数据,请求关闭

现代 TCP 首部还定义了与拥塞通知等机制有关的其他标志位,但理解连接建立、数据传输和释放时,SYN、ACK、FIN、RST 最为关键。

需要注意:PSH 不是"强制网络加速",URG 也不是通用的应用消息优先级系统。它们有明确的 TCP 语义,实际应用不能把标志名称按日常语言随意理解。

6. 窗口

窗口字段占 16 位,用于通告本端当前的接收能力。

它表达的是:从当前确认号开始,接收方还允许对方发送多少字节。

例如:

text 复制代码
确认号 = 701
窗口值 = 1000

表示接收方下一步期待 701,并为从 701 开始的 1000 字节保留了接收空间。对方可以据此调整发送窗口。

TCP 还可以在建立连接时协商窗口扩大选项,使实际接收窗口突破首部 16 位字段直接表示的范围。

7. 校验和

TCP 校验和字段占 16 位,覆盖 TCP 首部和数据。计算时还会加入 IP 伪首部。

在 IPv4 情况下,伪首部包含源地址、目的地址、协议号和 TCP 长度等信息。TCP 在 IP 中的协议号是 6。

伪首部只参与校验和计算,不会作为 TCP 首部的一部分在线路上传输。

与 UDP 不同,TCP 校验和不能选择省略。

8. 紧急指针

紧急指针字段占 16 位,并在 URG 标志有效时参与表示紧急数据边界。

这一机制在现代应用中使用较少,但仍属于经典 TCP 首部结构。它不能简单理解成让某个报文段绕过拥塞控制或获得网络设备的绝对优先转发。

9. 选项与填充

TCP 选项用于扩展基本首部能力,常见选项包括:

选项 主要作用
MSS 协商单个报文段可承载的最大数据长度
窗口扩大 扩大接收窗口可表示范围
时间戳 辅助测量 RTT,并处理高速网络中的序号回绕问题
SACK 告知发送方已经收到哪些不连续字节块

TCP 首部需要保持为 4 字节的整数倍,因此选项长度不足时会使用填充字节对齐。

八、用一个双向传输例子理解序号与确认号

假设客户端和服务器已经建立 TCP 连接。

客户端发送一个报文段:

text 复制代码
序号 = 1001
数据长度 = 100 字节

它携带的字节范围是:

text 复制代码
1001 ~ 1100

服务器连续收到这些数据后,可以回复:

text 复制代码
确认号 = 1101

意思是:"1100 及之前的连续字节已经收到,接下来请从 1101 开始。"

如果服务器此时也有 50 字节数据要发给客户端,它可以发送:

text 复制代码
服务器序号 = 5001
服务器确认号 = 1101
数据长度 = 50 字节

同一个报文段同时完成两件事:

  1. 发送服务器方向的新数据;
  2. 确认客户端方向已经收到的数据。

这就是全双工通信中的捎带确认。

需要强调,客户端序号 1001 和服务器序号 5001 属于两个独立的字节编号空间,不能放在同一条连续编号线上比较。

九、TCP 与 UDP 的整体对比

对比项 TCP UDP
连接方式 面向连接 无连接
通信形式 一对一 可配合单播、多播和广播
向应用提供的数据形式 连续字节流 独立数据报
是否保留应用写入边界 不保留 保留
可靠性 提供确认、重传、排序、去重等机制 不内置可靠交付机制
全双工 支持 UDP 套接字也可双向收发,但没有 TCP 连接语义
流量控制
拥塞控制 无内置拥塞控制
固定首部长度 最少 20 字节 8 字节
状态维护 两端维护连接状态 不维护 TCP 式连接状态
典型用途 Web 传输、文件传输、邮件、数据库连接 实时媒体、查询、设备发现及自定义传输协议

选择 TCP 还是 UDP,不是简单比较"谁更快",而是判断应用需要怎样的通信抽象,以及愿意把多少控制工作交给传输协议。

十、几个常见误区

1. "建立 TCP 连接后,网络中就有了一条专用线路"

TCP 连接是两端维护状态形成的逻辑关系。报文段仍然封装在 IP 数据报中,与其他流量共享路由器和链路。

2. "TCP 可靠,所以连接永远不会失败"

TCP 能检测和恢复许多丢包、乱序和重复问题,但无法保证在设备断电或网络长期中断时仍然完成传输。无法恢复时,TCP 会向应用报告错误。

3. "面向字节流就是一次发送一个字节"

字节流描述的是编号和应用接口语义。TCP 实际仍然把多个字节装入报文段发送。

4. "一次 send 对应接收方的一次 recv"

TCP 不保留应用写入边界。一次写入可能被拆分,多次写入也可能在接收方一次读取中出现。应用必须自行设计消息边界。

5. "确认号 N 表示只收到了第 N 个字节"

确认号 N 通常表示 N 之前的连续字节已经收到,现在期望 N。它体现的是累计确认,而不是孤立确认某个字节。

6. "窗口大小就是一个 TCP 报文段的大小"

窗口描述允许在途的数据范围,MSS 描述一个报文段中数据部分的上限。一个窗口通常包含多个报文段。

7. "TCP 可靠,所以天然安全"

可靠传输不等于加密、身份认证或防篡改攻击。TCP 本身不会隐藏应用数据。HTTPS 等安全通信还需要 TLS 提供机密性、完整性保护和身份认证。

8. "全双工需要建立两条 TCP 连接"

一条 TCP 连接本身就支持两个方向同时发送数据。两个方向拥有独立序号空间,但仍属于同一条连接。

十一、总结

TCP 在不可靠的 IP 服务之上,为应用建立了一条可靠字节流抽象:

  1. TCP 面向连接,但连接是端系统维护的逻辑状态,不是物理专线;
  2. 每条 TCP 连接只有两个端点,并通过四元组与其他连接区分;
  3. TCP 使用校验、序号、确认、重传和缓存实现可靠、按序、不重复的字节交付;
  4. TCP 支持全双工通信,两个方向拥有独立的字节序号空间;
  5. TCP 面向字节流,不保留应用每次写入的消息边界;
  6. TCP 以报文段为实际传输单元,字节流会根据 MSS、窗口和网络状态被分段发送;
  7. TCP 首部中的序号、确认号、标志位、窗口、校验和和选项,共同支撑连接管理与可靠传输;
  8. TCP 可靠不等于永不失败,也不等于自带加密和身份认证。

现在我们已经看到了 TCP 可靠传输所需的主要零件,但还没有回答它们为什么有效。确认丢了怎么办?超时应该设多长?序号怎样避免重复数据?发送方为什么不能每发一个报文段都停下来等待?接下来将从可靠数据传输的一般原理出发,理解确认、超时、重传与序号如何共同对抗不可靠信道。

如果这篇文章对你有帮助,欢迎点赞、评论、关注、收藏。你们的支持是我前进的动力!

相关推荐
OIDCAT2 小时前
国产USB转千兆网卡芯片CH398-RTL8153国产替代实测与选型
网络·嵌入式硬件·智能硬件·国产芯片·usb转千兆网卡
代码的小搬运工3 小时前
KVO学习
学习·ios·cocoa
辣知4 小时前
辣知·化智47 黄帝陵与民族精神归途
学习
知识分享小能手4 小时前
深度学习学习教程,从入门到精通,数值计算 — 知识点详解(4)
人工智能·深度学习·学习
M--Y4 小时前
Docker数据持久化与网络架构
网络·docker·架构
zyf1044165 小时前
暑期实践日志 Day49:复盘题库出题成果,夯实章节考点
学习·计算机网络·剪辑·暑期实践·课题任务
kaixin_啊啊5 小时前
多模态学习
学习
myy-learn5 小时前
30-HTTP协议
网络·网络协议·http