《Linux 网络编程》深入理解 TCP 协议(三):TCP 报头六大标志位详解
2026/9/12 21:13:25 网站建设 项目流程

🔥小叶-duck:个人主页

❄️个人专栏:《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》
《Linux系统从入门到实践》《Linux网络从入门到实践》
《Qt 方寸极境》 《MySQL》

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


目录

前言

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

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

1.2 ACK 标志位(确认标志)

1.2.1 ACK 是什么?

1.2.2 ACK 在 TCP 体系中的意义

1.3 SYN 标志位(同步标志)

1.3.1 SYN 标志基础

1.3.2 初识三次握手流程

1.4 FIN 标志位(结束标志)

1.4.1 FIN 标志基础

1.4.2 初识四次挥手流程

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

1.5.1 c

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

1.6.1 PSH 标志基础

1.6.2 PSH 的工作机制

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

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

1.7.1 URG 标志基础

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

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

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

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 报文作用:连接异常、两端状态不一致时,强制重置连接,重新握手

客户端 服务端 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 协议的内容还远没有结束,连接管理、滑动窗口、拥塞控制等更多核心机制,会在后续文章继续展开。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询