《Linux 网络编程》深入理解 TCP 协议(三):TCP 报头六大标志位详解

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》

《Linux系统从入门到实践》《Linux网络从入门到实践》

《Qt 方寸极境》 《MySQL》

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

前言

[一、TCP 报头标志位字段详解](#一、TCP 报头标志位字段详解)

[1.1 标志位本质与内核结构体定义](#1.1 标志位本质与内核结构体定义)

[1.2 ACK 标志位(确认标志)](#1.2 ACK 标志位(确认标志))

[1.2.1 ACK 是什么?](#1.2.1 ACK 是什么?)

[1.2.2 ACK 在 TCP 体系中的意义](#1.2.2 ACK 在 TCP 体系中的意义)

[1.3 SYN 标志位(同步标志)](#1.3 SYN 标志位(同步标志))

[1.3.1 SYN 标志基础](#1.3.1 SYN 标志基础)

[1.3.2 初识三次握手流程](#1.3.2 初识三次握手流程)

[1.4 FIN 标志位(结束标志)](#1.4 FIN 标志位(结束标志))

[1.4.1 FIN 标志基础](#1.4.1 FIN 标志基础)

[1.4.2 初识四次挥手流程](#1.4.2 初识四次挥手流程)

[1.5 RST 标志位(复位连接的 "紧急刹车")](#1.5 RST 标志位(复位连接的 “紧急刹车”))

[1.5.1 c](#1.5.1 c)

[1.6 PSH 标志位(催促数据交付的 "加急快递")](#1.6 PSH 标志位(催促数据交付的 “加急快递”))

[1.6.1 PSH 标志基础](#1.6.1 PSH 标志基础)

[1.6.2 PSH 的工作机制](#1.6.2 PSH 的工作机制)

[1.6.3 边界与局限:PSH 不是万能的强制读取指令](#1.6.3 边界与局限:PSH 不是万能的强制读取指令)

[1.7 URG 标志位(优先处理的 "绿色通道 ")](#1.7 URG 标志位(优先处理的 “绿色通道 ”))

[1.7.1 URG 标志基础](#1.7.1 URG 标志基础)

[1.7.2 TCP 报头 16 位紧急指针原理](#1.7.2 TCP 报头 16 位紧急指针原理)

[1.7.3 实战坑点:BSD Socket MSG_OOB 实现问题](#1.7.3 实战坑点:BSD Socket MSG_OOB 实现问题)

[1.7.4 URG、PSH、RST 简单区分](#1.7.4 URG、PSH、RST 简单区分)

结束语


前言

在上一篇文章中,我们解析了 TCP 首部长度字段、确认应答和超时重传,搭建起 TCP 可靠传输的基础框架。而 TCP 报文头部还有一组十分关键的控制字段 ------ 六大标志位,连接建立、数据推送、紧急指令、连接断开等全部核心行为,都依靠它们来驱动。SYN、ACK、FIN、RST、PSH、URG 各自承担不同职责,很多网络编程面试题、线上连接异常问题,根源都在于对标志位理解不到位。

本文将继续顺着 TCP 协议的学习脉络,逐个拆解六大标志位的含义、工作场景,结合 Linux 内核结构体定义,讲解三次握手、四次挥手的基础流程,同时区分 PSH 推送提醒与 URG 紧急带外数据的差异,分析 Socket 接口实现中紧急数据的实战坑点。全部内容结合协议规范与内核行为,兼顾原理理解和面试实战。

一、TCP 报头标志位字段详解

1.1 标志位本质与内核结构体定义

TCP 报头一共定义 8 个标志位,日常协议交互中重点使用其中 6 个。每一个标志位仅占用1 比特 ,是 C 语言位段的典型应用。标志位的核心作用 就是区分报文类型告诉接收端该如何处理当前收到的 TCP 报文

建立连接、断开连接、数据应答、紧急数据等不同类型报文,就是依靠将对应标志位置 1 来完成类型区分。

我们来看 Linux 内核tcphdr结构体里标志位的定义,源码位于**include/linux/tcp.h**:

cpp 复制代码
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;
};

补充说明:这里使用条件编译__LITTLE_ENDIAN_BITFIELD,是因为大小端平台下位段在内存排布顺序不一样,内核通过这种写法,保证不同 CPU 架构下标志位定义都能正常解析。

1.2 ACK 标志位(确认标志)

1.2.1 ACK 是什么?

ACK 全称acknowledgment,本质是确认序号字段的开关

  • ACK=0:代表这份报文不是应答报文,TCP 头部的 32 位确认序号失去意义,内核会直接忽略 ack_seq 字段。
  • ACK=1:代表报文中确认序号字段有效,接收端内核必须读取并解析确认序号,代表这个报文携带了对之前收到数据的确认。

除连接建立阶段最初的 SYN 报文之外,后续 TCP 传输过程中几乎所有报文,ACK 标志都会置为 1 。即便当前报文主要用来发送业务数据,TCP 也会顺便捎带上对过往数据的确认,这就是 TCP 的捎带应答机制。

TCP 是全双工对等通信 ,通信双方地位完全平等,两端都拥有独立的发送缓冲区与接收缓冲区,任意一方都可以主动发送数据,同时也要承担应答对方数据的任务,ACK 标志就是实现双向应答的基础。

对比:HTTP 属于非对等的主从架构,客户端主动发起请求,服务端被动响应;而 TCP 两端没有绝对的客户端、服务端身份限制,连接建立后两端对等。

1.2.2 ACK 在 TCP 体系中的意义

ACK 标志是六个标志里最核心的一个。网络传输存在丢包、乱序等不可靠问题,TCP 依靠确认应答 + 超时重传实现可靠传输;而全双工、流水线并行传输场景下,TCP 依靠序号、确认序号与 ACK 标志,完成对数据的确认、推进接收窗口,形成整套可靠传输闭环。

1.3 SYN 标志位(同步标志)

1.3.1 SYN 标志基础

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

TCP 属于面向连接的协议,必须先建立连接,才能开始传输业务数据,连接建立流程依靠 SYN 标志完成三次握手。

1.3.2 初识三次握手流程

  1. 第一次握手:客户端向服务端发送 SYN 报文(无应用数据,仅 TCP 报头),请求建立连接;
  2. 第二次握手:服务端收到 SYN 后,返回 SYN+ACK 合并报文,既确认客户端建连意愿,也表达自身建连意愿;
  3. 第三次握手:客户端收到 SYN+ACK 后,返回 ACK 应答报文,确认服务端意愿,双方连接进入 ESTABLISHED 已建立状态。

注意:通信过程中不会单独传输 SYN、ACK 比特位,只是 TCP 报头中把对应标志位置 1,底层依旧是标准 TCP 报文传输。
本篇文章仅做初识介绍,后续在 TCP 连接管理章节,会深入讲解连接相关系统调用、三次握手的本质,以及为什么需要三次握手,还有 "逻辑上本质是四次握手" 这一知识点。

1.4 FIN 标志位(结束标志)

1.4.1 FIN 标志基础

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

TCP 是全双工通信,读写两个方向通道相互独立。数据传输完成后,依靠 FIN 标志位断开 TCP 连接,标准流程为四次挥手,双方需要各自关闭属于自己的读写通道。

1.4.2 初识四次挥手流程

  1. 第一次挥手:主动关闭方调用 close,发送 FIN 报文,告知对方本端不再发送数据;
  2. 第二次挥手:被动关闭端返回 ACK 应答,确认收到关闭请求,此时被动端仍然可以继续发送缓冲区里剩余的数据;
  3. 第三次挥手:被动端数据全部发送完毕后,发送 FIN 报文,告知主动端自身也准备关闭发送通道;
  4. 第四次挥手:主动端返回 ACK 应答,确认关闭。主动端会进入 TIME_WAIT 状态,等待足够时长之后,双方连接彻底释放,变为 CLOSED 状态。

涉及的状态流转:FIN_WAIT1、FIN_WAIT2、CLOSE_WAIT、LAST_ACK、TIME_WAIT、CLOSED。

注意:通信过程中不会单独传输 FIN、ACK 比特位,只是 TCP 报头中把对应标志位置 1,底层依旧是标准 TCP 报文传输。
本节仅做初识介绍,后续 TCP 连接管理章节,会重新深入剖析四次挥手,讲解握手只需要三次而挥手需要四次的根本原因,以及为什么三次握手可以看作合并后的四次握手,但四次挥手一般情况无法简化合并成三次挥手。

1.5 RST 标志位(复位连接的 "紧急刹车")

1.5.1 c

  • 作用:强制复位、中断 TCP 连接,相当于连接的紧急刹车。
  • 使用场景:两端连接状态不一致、连接发生异常时。
  • 含义:当RST=1,代表告知对方直接终止本次连接,无需走四次挥手的正常关闭流程。

RST 全称 Reset 复位,携带 RST 标识的报文叫做复位报文段,核心目标是解决通信双方连接状态不一致的问题。三次握手有一处天然短板:最后一个 ACK 报文没有对应的应答确认机制 ,容易出现两端认知不一致

典型场景:三次握手最后一个 ACK 报文丢失

  • 客户端视角:发出最后一个 ACK,直接切换到 ESTABLISHED 状态,认定连接成功,能够发送业务数据。
  • 服务端视角:ACK 报文丢失,服务端停留在 SYN_RCVD 状态,判定握手尚未完成,连接没有建立。

此时客户端继续发送业务数据,服务端会判定报文非法,直接回复 RST 复位报文,通知客户端连接异常,需要重新发起连接建立。

重要特性 :RST 报文不需要接收方返回 ACK 确认。发送方发出 RST 之后直接断开连接;接收方收到 RST 报文会立刻释放连接资源,不会进入 TIME_WAIT 状态

RST 报文作用:连接异常、两端状态不一致时,强制重置连接,重新握手

bash 复制代码
客户端                  服务端
 SYN  --------->
        <--------- SYN+ACK
 ACK  ---------> 【ACK丢失】
客户端:连接建立成功
服务端:握手未完成

客户端发业务数据 --------->
        <--------- RST
客户端重新建立连接

1.6 PSH 标志位(催促数据交付的 "加急快递")

1.6.1 PSH 标志基础

  • 作用:提醒接收端应用程序尽快从 TCP 缓冲区读取数据,降低端到端的交互延迟,并非强制指令。
  • 使用场景:交互型连接、小数据需要及时交付的场景,典型如 SSH、Telnet 远程终端。
  • 含义:PSH=1,发送方告知接收方,希望收到报文后尽快把缓冲区数据交付给应用层,不用等待缓冲区填满、达到水位线再交付

PSH 全称 Push,可以理解为 TCP 里的加急提醒。它不会建立连接、确认报文或者关闭连接,只用来调整数据交付的时机。

TCP 为提升传输效率,默认会合并小包发送,接收端也会等待缓冲区积攒一定数据量后,再一次性交给应用层。这种策略适合大批量文件传输,但交互式命令场景会产生明显延迟,PSH 标志就是用来解决这类延迟问题。

1.6.2 PSH 的工作机制

发送方设置 PSH 标志时包含两层意图:

  • 发送端:立刻发送当前缓冲区的数据,不再攒包等待;
  • 接收端:收到带 PSH 的报文后,尝试尽快唤醒等待数据的应用进程,把缓冲区数据交付给应用层。

重点:PSH只是提醒,不能强制 接收方读取缓冲区数据。数据交付最终由操作系统 TCP 协议栈调度。收到 PSH 报文,内核只会把对应数据标记为可交付状态,尝试触发 read 就绪条件,而非强制应用程序执行读取操作。

操作系统 TCP 协议栈内部维护缓冲区水位线机制 ,分为低水位SO_RCVLOWAT高水位阈值 。只有接收缓冲区的数据量达到低水位,read、epoll 等才会判定套接字可读就绪,唤醒阻塞等待的应用。

PSH 的作用,是临时降低这个就绪门槛 ,协议栈可忽略低水位约束 ,直接触发 read 就绪事件;但仅仅是就绪通知,不代表应用程序一定会马上读取

常见误解:收到 PSH,应用必须立刻读。PSH 仅为提醒,内核仅触发就绪事件,读取行为由应用代码决定。

1.6.3 边界与局限:PSH 不是万能的强制读取指令

PSH 的所有效果,都建立在应用层已经调用read,或者通过 epoll/select 监听套接字事件的前提之上

如果应用程序本身没有调用 read 读取数据,例如业务逻辑死循环、没有把 read 放入事件循环: PSH 报文抵达 → 内核发出就绪通知 → 应用没有监听等待 → 数据持续堆积在接收缓冲区

哪怕触发低水位,也没有等待的进程可以唤醒。此时无论发送多少携带 PSH 的报文,都无法推动数据读取,数据会一直留在内核缓冲区,直到连接关闭或程序崩溃。这类问题本质属于应用程序 bug,并非 TCP 协议或者发送端的问题。

1.7 URG 标志位(优先处理的 "绿色通道 ")

1.7.1 URG 标志基础

  • 作用:标识 TCP 报头内16 位紧急指针字段是否有效,用来实现带外紧急数据传输,给控制指令开辟优先处理通道。
  • 使用场景:远程终端中断、传输任务启停控制,典型例子:SSH 里Ctrl+C中断远端程序、网盘暂停 / 取消上传指令。
  • 含义:常规通信URG=0,紧急指针无效 ;当**URG=1,代表当前报文包含紧急数据**,接收端可以跳过普通数据流排队,优先处理紧急控制信息。

TCP 默认数据按序号顺序排队交付给应用层。但部分控制指令不能等待前面大批量数据处理完毕,URG 标志搭配紧急指针,就实现了这种带外优先处理能力。

1.7.2 TCP 报头 16 位紧急指针原理

紧急指针是相对于当前报文数据区起始位置的偏移量,仅作用在当前这一个 TCP 报文段,和全局缓冲区、整条连接的全局序号不产生关联。 规则:

  1. 紧急数据从当前报文数据区第一个字节开始;
  2. 紧急指针的数值等于紧急数据总字节长度,指向紧急数据最后一个字节;
  3. 紧急数据区间:[本报文起始序号,本报文起始序号 + 紧急指针 − 1];区间内为紧急数据,后面剩余部分属于普通数据。

示例: 报文起始序号:1000,数据区总长 100 字节,紧急指针值:10

  • 紧急数据区间:1000 ~ 1009(前 10 字节)
  • 普通数据区间:1010 ~ 1099(后 90 字节)

接收端检测到URG=1,会优先把这一段紧急数据递交给应用层,不需要在普通缓冲区排队等待。

易混淆点:计算紧急数据末尾序号时,公式起始序号 + 紧急指针 −1 看起来和全局序号相关,但紧急指针本身只代表当前报文内部 的数据长度;后续报文的紧急数据,由各自报文的紧急指针独立定义,不会复用之前的偏移

1.7.3 实战坑点:BSD Socket MSG_OOB 实现问题

协议层面,紧急指针由于大小是16位 bit,理论可以标记最长 65535 字节的紧急数据 。但 BSD Socket 的经典实现存在历史遗留问题: 接收端使用MSG_OOB标记读取带外数据时,只能读到紧急数据的最后一个字节 ,紧急数据前面其余字节会混入普通数据流,需要用普通 recv 读取。

示例: 发送方调用:send(sockfd, "ABCD", 4, MSG_OOB)

  • 内核生成URG=1报文,紧急指针 = 4指向字符D
  • 接收端:recv(sockfd, &buf, 1, MSG_OOB)仅读到D
  • ABC被当作普通数据,通过普通 recv 读取

注意:这属于 Socket 接口实现层面的历史遗留问题不是 TCP 协议本身的限制

工程开发建议:尽量不依赖 TCP 紧急数据。优先采用双连接方案:一条连接传输业务数据,一条独立连接传输控制指令,稳定性更好。

1.7.4 URG、PSH、RST 简单区分

  • URG:标记带外紧急数据,让控制指令优先处理,开辟数据绿色通道;
  • PSH:提醒内核尽快将缓冲区数据交付应用层,只是交付时机的提示,不区分紧急数据流;
  • RST:强制立刻断开连接,终止本次 TCP 会话,不需要四次挥手。

结束语

本篇我们逐一分析了 TCP 报头里六个核心标志位,从 ACK、SYN、FIN,到 RST、PSH、URG,梳理了它们各自的作用、底层逻辑以及对应的连接交互流程。SYN 与 FIN 支撑起连接建立与关闭的完整生命周期,RST 作为异常重置手段快速释放故障连接,PSH 用来通知内核尽快向上交付数据,而 URG 紧急指针机制,则实现了带外数据优先传输的能力。

标志位是 TCP 报文的控制开关,理解它们,才能看懂抓包文件里的报文交互,定位断连、连接重置等各类网络异常。但 TCP 协议的内容还远没有结束,连接管理、滑动窗口、拥塞控制等更多核心机制,会在后续文章继续展开。

相关推荐
Android系统攻城狮2 小时前
Linux Gstreamer深度解析之gst_audio_channel_positions_to_mask调用流程与实战(十七)
linux·运维·服务器·gstreamer音视频·音视频进阶
华允物联-HUAIOT2 小时前
什么是工业路由器?和普通家用路由器的区别
网络·智能路由器
为思念酝酿的痛3 小时前
应用层协议HTTPS
网络·网络协议·http·https
小程序设计3 小时前
基于某数据管理系统的目标遍历漏洞成因与防御设计技术与实现
运维·网络
33三 三like3 小时前
Jiangxi
linux·运维·服务器
anxiao_m3 小时前
局域网文件传输工具有哪些?4款主流工具实测对比测评
大数据·网络·数据库
IPdodo_3 小时前
curl 如何测试代理 IP?HTTP、SOCKS5 与认证参数示例
前端·网络·python·https·网络调试
念越3 小时前
Python基础语法(一):变量、类型与程序控制流
开发语言·网络·python
wuminyu3 小时前
Kafka的写入延迟与磁盘IO抖动原理分析
java·linux·c语言·jvm·c++