【Linux网络】深入理解 TCP 协议(二):序号机制、流量控制与连接管理


🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!


🎬 博主简介:


文章目录

  • 前言:
  • [一. TCP 核心知识点回顾](#一. TCP 核心知识点回顾)
  • [二. TCP 发送数据的两种工作模式](#二. TCP 发送数据的两种工作模式)
    • [2.1 串行发送模式(停等协议)](#2.1 串行发送模式(停等协议))
    • [2.2 并行发送模式(流水线模式)](#2.2 并行发送模式(流水线模式))
  • [三. 序号与确认序号的深度解析](#三. 序号与确认序号的深度解析)
    • [3.1 序号的本质](#3.1 序号的本质)
    • [3.2 确认序号的定义](#3.2 确认序号的定义)
    • [3.3 序号的三大核心功能](#3.3 序号的三大核心功能)
    • [3.4 为什么需要同时有序号和确认序号?](#3.4 为什么需要同时有序号和确认序号?)
  • [四. 16 位窗口大小与流量控制](#四. 16 位窗口大小与流量控制)
    • [4.1 窗口大小的含义](#4.1 窗口大小的含义)
    • [4.2 流量控制的工作流程](#4.2 流量控制的工作流程)
    • [4.3 窗口探测机制](#4.3 窗口探测机制)
  • [五. 标志位字段详解](#五. 标志位字段详解)
    • [5.1 ACK 标志(确认标志)](#5.1 ACK 标志(确认标志))
    • [5.2 SYN 标志(同步标志)](#5.2 SYN 标志(同步标志))
    • [5.3 FIN 标志(结束标志)](#5.3 FIN 标志(结束标志))
  • [六. TCP 连接管理机制](#六. TCP 连接管理机制)
    • [6.1 三次握手建立连接](#6.1 三次握手建立连接)
    • [6.2 四次挥手断开连接](#6.2 四次挥手断开连接)
  • 结尾:

前言:

在上一篇文章中,我们深度解析了 TCP 报头的设计精髓以及可靠性的两大基石 ------ 确认应答与超时重传机制。但 TCP 的复杂之处远不止于此:为了提高传输效率,TCP 支持并行发送多个报文;为了避免发送方过快导致接收方溢出,TCP 引入了流量控制;为了管理连接的生命周期,TCP 设计了经典的三次握手和四次挥手流程。本文将继续沿着 TCP 协议的设计思路,从发送模式讲起,深度拆解序号与确认序号的核心作用,详解流量控制机制,并结合 Linux 内核源码和实战案例,彻底搞懂 TCP 连接管理的底层原理。所有内容均严格基于 TCP 协议规范和 Linux 内核实现,力求做到理论与实践相结合。


一. TCP 核心知识点回顾

在开始新内容之前,我们先回顾几个关键结论,这些是理解后续内容的基础:

  • TCP 通信双方交换的是完整的 TCP 报文:即使是单纯的应答,也至少包含一个完整的 TCP 报头,不会只发送单独的标志位。
  • 可靠性的本质不是 "必须送达":而是 "无论发送成功还是失败,发送方都能知道结果"。收到应答表示成功,超时未收到应答则判定为失败并重传。
  • TCP 是全双工协议:通信双方可以同时发送和接收数据,这一特性深刻影响了 TCP 的很多设计,包括捎带应答、三次握手等。

二. TCP 发送数据的两种工作模式

TCP 提供了两种数据发送模式,分别适用于不同的数据量场景:

2.1 串行发送模式(停等协议)

这是最简单的发送模式:发送方发送一个报文后,必须等待接收方的 ACK 应答,才能发送下一个报文。

html 复制代码
主机A                主机B
   |---- 数据1 ---->|
   |                |
   |<---- ACK1 -----|
   |                |
   |---- 数据2 ---->|
   |                |
   |<---- ACK2 -----|

优点:实现简单,不会出现乱序和重复问题。

缺点:效率极低,尤其是在网络往返时间(RTT)较长的情况下,大部分时间都在等待应答。

这种模式只适用于发送少量数据的场景,在实际网络通信中很少单独使用。

2.2 并行发送模式(流水线模式)

这是 TCP 最主流的发送模式:发送方可以在未收到前一个报文的 ACK 应答时,连续发送多个报文。

html 复制代码
主机A                主机B
   |---- 数据1 ---->|
   |---- 数据2 ---->|
   |---- 数据3 ---->|
   |                |
   |<---- ACK1 -----|
   |<---- ACK2 -----|
   |---- 数据4 ---->|
   |<---- ACK3 -----|

优点:将多个报文的等待时间重叠,大幅提高了网络吞吐量和传输效率。

问题:并行发送带来了新的挑战 ------ 如何区分哪个报文丢了?如何保证数据按序到达?如何处理重复报文?

为了解决这些问题,TCP 引入了两个核心字段:32 位序号32 位确认序号


三. 序号与确认序号的深度解析

序号和确认序号是 TCP 可靠性和高效性的核心,几乎所有的 TCP 机制都建立在这两个字段之上。

3.1 序号的本质

TCP 将发送缓冲区中的数据看作一个连续的字节数组,序号本质上就是这个字节数组的下标

每个 TCP 报文的序号字段,填写的是该报文第一个数据字节的下标。例如:

  • 发送缓冲区第 1~1000 字节组成的报文,序号为 1
  • 第 1001~2000 字节组成的报文,序号为 1001
  • 第 2001~3000 字节组成的报文,序号为 2001

3.2 确认序号的定义

确认序号的定义非常关键:确认序号 = 收到的最后一个字节的序号 + 1

它的含义是:我已经收到了确认序号之前的所有字节,下一次请从确认序号开始发送

例如:

  • 主机 B 收到了序号为 1~1000 的字节,回复确认序号 1001
  • 主机 B 收到了序号为 1001~2000 的字节,回复确认序号 2001

重要特性:累积确认

如果主机 B 收到了 1~1000 和 2001~3000 字节,但没有收到 1001~2000 字节,它只会回复确认序号 1001,而不会确认 2001~3000 字节。这保证了 TCP 数据的按序交付。

3.3 序号的三大核心功能

序号机制解决了并行发送带来的所有问题:

  • 保证可靠性:通过确认序号,发送方可以准确知道哪些数据已经被接收,哪些需要重传。
  • 数据去重:由于超时重传,接收方可能会收到重复的报文。通过序号,接收方可以轻松识别并丢弃重复报文。
  • 保证按序到达:网络传输可能导致报文乱序,接收方可以根据序号对报文进行排序,然后按序交付给应用层。

3.4 为什么需要同时有序号和确认序号?

很多初学者会问:只用一个序号字段不行吗?应答时直接把序号 + 1 返回不就可以了?

答案在于 TCP 的全双工特性捎带应答机制

捎带应答:当主机 B 需要给主机 A 发送数据时,它可以把对主机 A 之前数据的 ACK 应答,"搭顺风车" 放在自己的数据报文中一起发送。这样可以减少网络报文的数量,提高效率。

例如:

  • 主机 A 给主机 B 发送数据(序号 1~1000)
  • 主机 B 正好有数据要发给主机 A,它可以在自己的数据报文中,同时设置确认序号 1001 和自己的序号(比如 5001~6000)

这种情况下,一个报文既是数据报文,又是应答报文,必须同时包含序号(自己发送数据的序号)和确认序号(对对方数据的确认)。


四. 16 位窗口大小与流量控制

即使网络带宽足够大,发送方也不能无限制地发送数据,因为接收方的处理能力是有限的。如果发送方发送太快,导致接收方的接收缓冲区被填满,后续的数据会被直接丢弃,造成不必要的网络资源浪费。

为了解决这个问题,TCP 引入了流量控制机制,而 16 位窗口大小字段就是流量控制的核心。

4.1 窗口大小的含义

窗口大小字段表示接收方当前接收缓冲区的剩余空间大小,单位是字节。

接收方在发送 ACK 应答时,会将自己接收缓冲区的剩余空间大小填入窗口大小字段,告诉发送方自己还能接收多少数据。

发送方根据这个值,调整自己的发送速度:

  • 窗口大:接收方处理能力强,发送方可以加快发送速度
  • 窗口小:接收方处理能力弱,发送方需要减慢发送速度
  • 窗口为 0:接收方缓冲区已满,发送方必须停止发送数据

4.2 流量控制的工作流程

html 复制代码
主机A(发送方)        主机B(接收方,缓冲区大小8192字节)
   |                     |
   |---- 数据1~1000 ---->|  缓冲区剩余:7192字节
   |                     |
   |<-- ACK(1001, 7192)-|  告诉A还能接收7192字节
   |                     |
   |-- 数据1001~5000 -->|  缓冲区剩余:3192字节
   |                     |
   |<-- ACK(5001, 3192)-|  告诉A还能接收3192字节
   |                     |
   |-- 数据5001~8192 -->|  缓冲区剩余:0字节
   |                     |
   |<--- ACK(8193, 0) --|  告诉A停止发送
   |                     |
   |                     |  应用层读取了4096字节,缓冲区剩余4096字节
   |                     |
   |<-- ACK(8193, 4096)-|  窗口更新,告诉A可以继续发送
   |                     |
   |- 数据8193~12288 -->|

4.3 窗口探测机制

当接收方的窗口为 0 时,发送方会停止发送数据。但如果后续接收方的窗口更新报文在传输过程中丢失了,发送方会一直等待,导致死锁。

为了避免这种情况,TCP 引入了窗口探测机制:当发送方收到窗口为 0 的 ACK 后,会定期发送一个只有 1 字节数据的窗口探测报文,询问接收方当前的窗口大小。


五. 标志位字段详解

TCP 报头中有 6 个关键的标志位,它们的本质是区分报文类型,告诉接收方应该如何处理这个报文。

我们先来看 Linux 内核中tcphdr结构体中标志位的定义(位于include/linux/tcp.h):

c 复制代码
struct tcphdr {
    __be16 source;
    __be16 dest;
    __be32 seq;
    __be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u16 res1:4,     // 保留位4位
          doff:4,     // 4位首部长度
          fin:1,      // FIN标志:关闭连接
          syn:1,      // SYN标志:建立连接
          rst:1,      // RST标志:重置连接
          psh:1,      // PSH标志:推送数据
          ack:1,      // ACK标志:确认号有效
          urg:1,      // URG标志:紧急指针有效
          ece:1,      // ECE标志:显式拥塞通知回显
          cwr:1;      // CWR标志:拥塞窗口减小
#elif defined(__BIG_ENDIAN_BITFIELD)
    __u16 doff:4,
          res1:4,
          cwr:1,
          ece:1,
          urg:1,
          ack:1,
          psh:1,
          rst:1,
          syn:1,
          fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
    __be16 window;
    __sum16 check;
    __be16 urg_ptr;
};

可以看到,每个标志位只占 1 个比特位,这是 C 语言位段特性的典型应用。下面我们重点讲解三个最核心的标志位:

5.1 ACK 标志(确认标志)

  • 作用:表示确认序号字段是否有效。
  • 使用场景:除了最初的 SYN 报文,所有 TCP 报文的 ACK 标志都应该置 1。
  • 含义:当 ACK=1 时,表示这个报文包含对之前收到数据的确认。

5.2 SYN 标志(同步标志)

  • 作用:请求建立连接。
  • 使用场景:在 TCP 三次握手的前两个报文中使用。
  • 含义:当 SYN=1 时,表示这是一个连接请求报文。

5.3 FIN 标志(结束标志)

  • 作用:通知对方本端要关闭连接。
  • 使用场景:在 TCP 四次挥手的过程中使用。
  • 含义:当 FIN=1 时,表示本端已经没有数据要发送了,请求关闭连接。

其他三个标志位的作用:

  • URG:紧急指针有效,表示报文中有紧急数据
  • PSH:提示接收端立即将数据从 TCP 缓冲区推送给应用层
  • RST:强制重置连接,用于处理异常情况

六. TCP 连接管理机制

TCP 是面向连接的协议,任何数据传输之前都必须先建立连接,数据传输完成后必须释放连接。TCP 连接的建立和释放分别通过三次握手四次挥手来完成。

6.1 三次握手建立连接

三次握手的完整流程

html 复制代码
客户端(CLOSED)                服务器(LISTEN)
    |                            |
    |---- SYN(seq=x) ---------->|  第一次握手:客户端请求建立连接
    |                            |
    |<-- SYN+ACK(seq=y, ack=x+1)-|  第二次握手:服务器确认连接请求
    |                            |
    |---- ACK(seq=x+1, ack=y+1)->|  第三次握手:客户端确认服务器的确认
    |                            |
    |(ESTABLISHED)             |(ESTABLISHED)

为什么是三次握手?(面试高频考点)

这是 TCP 连接管理中最经典的面试题,核心原因有两个:

  • 以最小次数验证全双工通信
    • 第一次握手:服务器知道客户端可以发送数据
    • 第二次握手:客户端知道服务器可以发送和接收数据
    • 第三次握手:服务器知道客户端可以接收数据

三次握手是验证全双工通信的最小次数。如果只有两次握手,服务器无法确认客户端是否能接收数据。

  • 以最小成本确认双方的通信意愿
    • 客户端发送 SYN:表示我想和你建立连接
    • 服务器发送 SYN+ACK:表示我同意和你建立连接
    • 客户端发送 ACK:表示我知道你同意了

三次握手完成了双方意愿的确认。如果只有两次握手,服务器无法确认客户端是否收到了自己的同意报文。

三次握手的状态转换

  • 客户端:CLOSED → SYN_SENT → ESTABLISHED
  • 服务器:CLOSED → LISTEN → SYN_RCVD → ESTABLISHED

6.2 四次挥手断开连接

四次挥手的完整流程

html 复制代码
主动关闭方(ESTABLISHED)        被动关闭方(ESTABLISHED)
    |                            |
    |---- FIN(seq=m) ----------->|  第一次挥手:主动关闭方请求关闭
    |                            |
    |<---- ACK(ack=m+1) --------|  第二次挥手:被动关闭方确认关闭请求
    |(FIN_WAIT_2)              |(CLOSE_WAIT)
    |                            |  被动关闭方继续发送剩余数据
    |                            |
    |<---- FIN(seq=n) ----------|  第三次挥手:被动关闭方数据发送完毕,请求关闭
    |                            |
    |---- ACK(ack=n+1) --------->|  第四次挥手:主动关闭方确认
    |(TIME_WAIT)               |(CLOSED)
    |                            |
    | 等待2MSL时间               |
    |                            |
    |(CLOSED)                  |

为什么是四次挥手?

建立连接时,服务器的 SYN 和 ACK 可以合并在一个报文中发送(捎带应答),但断开连接时不能合并,原因是:

  • 当主动关闭方发送 FIN 时,只表示主动关闭方没有数据要发送了
  • 被动关闭方可能还有数据没有发送完,它需要先发送 ACK 确认,然后继续发送剩余数据
  • 等被动关闭方的数据全部发送完毕后,它才会发送 FIN 表示自己也没有数据要发送了

因此,断开连接需要四次挥手。


结尾:

html 复制代码
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!

结语:本文我们深入讲解了 TCP 协议的核心机制:从并行发送模式引出序号和确认序号的作用,详解了流量控制的原理,结合 Linux 内核源码分析了标志位的实现,并彻底搞懂了三次握手和四次挥手的底层逻辑。TCP 协议的设计充满了智慧,它在不可靠的网络之上,通过一系列精巧的机制,实现了可靠、高效的数据传输。当然,TCP 的复杂之处还不止于此,为了进一步提高传输性能,TCP 还引入了滑动窗口、快速重传、拥塞控制等机制,这些内容我们将在后续的文章中继续拆解。如果本文对你有帮助,欢迎点赞、收藏、关注,我会持续分享更多 Linux 系统编程和网络编程的干货内容。

✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど

相关推荐
梁辰兴1 小时前
计算机网络基础:具有全分布式结构的 P2P 文件共享程序
网络·分布式·计算机网络·p2p·计算机网络基础·梁辰兴·文件共享程序
布朗克1681 小时前
28 网络编程——Socket、TCP/UDP与HttpClient
java·网络·tcp/ip·udp
云飞云共享云桌面2 小时前
SolidWorks服务器+云飞云共享云桌面 = 10人共享方案
linux·运维·服务器·网络·制造
Full Stack Developme10 小时前
JVM 与 Linux 交互的核心原理
linux·运维·jvm
皮皮学姐分享-ppx10 小时前
政府绿色采购数据库(2015-2024.3)
大数据·网络·数据库·人工智能·制造
Java识堂10 小时前
多级负载均衡架构
运维·架构·负载均衡
MXsoft61810 小时前
## 自动化巡检:从手工两小时到系统五分钟的落地实践
运维·自动化
ZLG_zhiyuan10 小时前
直击华南工博会|ZLG致远电子:EtherCAT与自动化总线应用方案动态实景呈现
运维·自动化
HackTwoHub10 小时前
最新Nessus2026.6.8版本主机漏洞扫描/探测工具Windows/Linux
linux·运维·服务器·安全·web安全·网络安全·安全架构