【TCP】TCP三次握手与四次挥手(系统性知识体系+对比表格)

文章目录

TCP三次握手与四次挥手

本文从前置基础、核心流程、本质问题、关键细节、拓展场景五个维度,构建完整的TCP连接管理知识体系,覆盖原理、标准、异常场景与面试核心考点。


一、前置基础:TCP核心特性与报文核心字段

所有握手/挥手机制,都是为TCP的核心设计目标服务的,先明确底层前提。

1.1 TCP的核心特性

TCP(传输控制协议)是面向连接、可靠、全双工、基于字节流的传输层协议,核心约束:

  • 面向连接:数据传输前必须先建立逻辑连接,传输完成后必须安全释放连接
  • 全双工:连接建立后,双方的读/写通道完全独立,可同时双向传输数据
  • 可靠传输:通过序号确认、超时重传、滑动窗口、校验和等机制,保证数据无丢失、无重复、按序到达
  • 连接本质:TCP连接并非物理链路,而是通信双方各自维护的一套连接状态(状态机、序号、窗口、选项等参数),通过报文交互同步状态,达成双向一致。

1.2 TCP报文核心头部字段(握手/挥手相关)

TCP头部固定20字节,可选选项最长40字节,核心关键字段如下:

字段 长度 核心作用
序号(SEQ) 32位 本报文段发送数据的第一个字节的编号,TCP为每个字节分配唯一序号;SYN/FIN报文即使无数据,也消耗1个序号
确认号(ACK) 32位 期望收到对方下一个报文的第一个字节序号;仅当ACK标志位为1时有效,连接建立后所有报文的ACK必须为1
控制标志位 6位 核心4个: • SYN:同步序号,用于连接建立时同步初始序号 • ACK:确认号有效,所有确认报文必须置1 • FIN:终止连接,发送方无数据要发送,关闭写通道 • RST:复位连接,异常中断无效连接
窗口大小 16位 滑动窗口大小,用于流量控制,告知对方自己的接收缓冲区剩余空间
选项 可变长度 握手时协商核心参数:MSS(最大报文段长度)、SACK(选择性确认)、窗口扩大因子、时间戳等

二、TCP三次握手/四次挥手(对比表格)

1、TCP三次握手全流程 结构化表格

(前提:服务端已进入LISTEN监听状态,客户端为连接发起方)

步骤序号 握手阶段 发送方 核心控制标志位 核心字段取值 发送方状态流转 接收方状态流转 核心业务作用 序号消耗规则
第一次握手 客户端同步请求 客户端 SYN=1 SEQ=ISN©(客户端随机初始序号) CLOSED → SYN_SENT 保持LISTEN不变 向服务端发起连接请求,同步客户端初始序号,协商TCP传输选项 无应用数据,强制消耗1个序号
第二次握手 服务端同步+确认 服务端 SYN=1、ACK=1 SEQ=ISN(s)(服务端随机初始序号) ACK=ISN©+1 LISTEN → SYN_RCVD 保持SYN_SENT不变 1. 确认客户端的SYN请求有效;2. 同步服务端自身初始序号,完成双向序号的单向同步 无应用数据,强制消耗1个序号
第三次握手 客户端最终确认 客户端 ACK=1 SEQ=ISN©+1 ACK=ISN(s)+1 SYN_SENT → ESTABLISHED SYN_RCVD → ESTABLISHED 确认服务端的SYN有效,完成全双工连接的双向同步与能力校验,连接正式建立 无应用数据不消耗序号;携带应用数据时,消耗对应数据长度的序号

2、TCP四次挥手全流程 结构化表格

(前提:双方均处于ESTABLISHED已连接状态,客户端为主动关闭方,服务端为被动关闭方)

步骤序号 挥手阶段 发送方角色 核心控制标志位 核心字段取值 发送方状态流转 接收方状态流转 核心业务作用 序号消耗规则
第一次挥手 主动方关闭请求 主动关闭方(客户端) FIN=1 SEQ=u(客户端已传输数据的最终序号+1) ESTABLISHED → FIN_WAIT_1 保持ESTABLISHED不变 请求关闭客户端→服务端的写通道,告知对方自身无新数据要发送 无应用数据,强制消耗1个序号
第二次挥手 被动方请求确认 被动关闭方(服务端) ACK=1 SEQ=v(服务端已传输数据的最终序号+1) ACK=u+1 ESTABLISHED → CLOSE_WAIT FIN_WAIT_1 → FIN_WAIT_2 确认收到主动方的关闭请求,告知对方停止发送数据;同时保留服务端→客户端的写通道,可继续传输剩余未发完的应用数据 无应用数据不消耗序号;携带应用数据时,消耗对应数据长度的序号
第三次挥手 被动方关闭请求 被动关闭方(服务端) FIN=1、ACK=1 SEQ=w(服务端CLOSE_WAIT阶段传输完剩余数据后的最终序号) ACK=u+1 CLOSE_WAIT → LAST_ACK 保持FIN_WAIT_2不变 请求关闭服务端→客户端的写通道,告知对方自身所有数据已传输完成,无新数据发送 无应用数据,强制消耗1个序号
第四次挥手 主动方最终确认 主动关闭方(客户端) ACK=1 SEQ=u+1 ACK=w+1 FIN_WAIT_2 → TIME_WAIT LAST_ACK → CLOSED 确认服务端的关闭请求,完成全双工双向通道的关闭;主动方进入TIME_WAIT状态等待2MSL后,最终进入CLOSED状态 无应用数据不消耗序号;携带应用数据时,消耗对应数据长度的序号

3、三次握手与四次挥手 核心差异对比表

对比维度 TCP三次握手 TCP四次挥手
核心目标 建立全双工可靠连接,同步双方初始序号与传输参数,校验双向收发能力 安全释放全双工连接,确保双方所有数据传输完成,无丢失、无残留
报文合并逻辑 服务端的SYN(同步自身序号)与ACK(确认客户端请求)无依赖,可合并为一个报文发送 被动关闭方的ACK(确认关闭请求)与FIN(自身数据传输完成)强依赖应用层数据传输,必须拆分两次发送,不可合并
状态机流转终点 双方最终进入ESTABLISHED(连接已建立)状态 双方最终进入CLOSED(连接已关闭)状态,主动关闭方需经过TIME_WAIT状态
报文序号特性 前两次握手强制消耗序号,第三次握手无数据不消耗 第一次、第三次挥手强制消耗序号,第二次、第四次挥手无数据不消耗
异常核心风险 SYN洪水攻击,半连接队列溢出导致正常连接无法建立 第四次挥手ACK丢失导致被动关闭方无法正常关闭、TIME_WAIT状态占用端口资源
流程简化可行性 不可简化为两次,四次为冗余操作 特殊场景(被动方无剩余数据)可合并ACK+FIN,简化为三次挥手

三、TCP三次握手全流程

三次握手的本质是全双工连接的双向同步与确认,核心目标有3个:

  1. 同步双方的初始序号ISN,为可靠字节流传输奠定编号基础
  2. 协商TCP核心传输选项(MSS、窗口缩放、SACK等)
  3. 确认双方的发送能力、接收能力均正常,完成双向能力校验

2.1 前提状态

  • 服务端:调用listen()系统调用,从CLOSED状态进入LISTEN(监听)状态,等待客户端连接请求
  • 客户端:处于CLOSED状态,准备发起连接

2.2 分步流程(含状态流转)

第一步:客户端发送SYN同步报文(第一次握手)
  • 客户端动作:调用connect(),随机生成自身初始序号ISN(c),发送SYN=1,SEQ=ISN© 的报文
  • 报文特性:不携带应用数据,消耗1个序号
  • 状态流转:客户端从CLOSEDSYN_SENT(同步已发送)
第二步:服务端回复SYN+ACK报文(第二次握手)
  • 服务端动作:收到客户端SYN报文后,完成两个核心动作:
    1. 确认客户端的SYN:置ACK=1,确认号ACK=ISN(c)+1
    2. 同步自身初始序号:随机生成ISN(s),置SYN=1SEQ=ISN(s)
  • 报文特性:不携带应用数据,消耗1个序号
  • 状态流转:服务端从LISTENSYN_RCVD(同步已接收)
第三步:客户端回复ACK确认报文(第三次握手)
  • 客户端动作:收到服务端SYN+ACK报文后,发送ACK=1,SEQ=ISN©+1,ACK=ISN(s)+1 的报文
  • 报文特性:可携带应用数据;若不携带数据,不消耗序号
  • 状态流转:
    • 客户端发送后,从SYN_SENTESTABLISHED(连接已建立)
    • 服务端收到该ACK后,从SYN_RCVDESTABLISHED
  • 结果:全双工TCP连接正式建立,双方可开始双向数据传输。

2.3 关键补充:初始序号ISN为什么是随机的?

RFC793规定ISN为基于时钟的递增计数器(每4微秒+1,约4.55小时循环一次),而非固定值,核心原因:

  1. 防止历史延迟报文被错误接收:避免上一次连接的残留报文,在新连接中被当成有效数据处理
  2. 提升安全性:防止TCP序号预测攻击,避免攻击者伪造合法报文干扰连接。

四、TCP四次挥手全流程

四次挥手的本质是全双工连接的双向安全关闭,核心目标:确保双方所有数据都传输完成,无数据丢失,同时安全释放连接资源。

核心前提:TCP全双工的读写通道独立,关闭一个方向的通道,不影响另一个方向的数据传输。

3.1 前提状态

双方均处于ESTABLISHED状态,假设客户端为主动关闭方服务端为被动关闭方(任意一方均可发起主动关闭)。

3.2 分步流程(含状态流转)

第一步:客户端发送FIN终止报文(第一次挥手)
  • 客户端动作:调用close()/shutdown(),发送FIN=1,SEQ=u 的报文(u为客户端已发送数据的最后一个字节序号+1)
  • 报文含义:客户端无数据要发送,请求关闭客户端→服务端的写通道
  • 报文特性:不携带应用数据,消耗1个序号
  • 状态流转:客户端从ESTABLISHEDFIN_WAIT_1(终止等待1)
第二步:服务端回复ACK确认报文(第二次挥手)
  • 服务端动作:收到FIN报文后,发送ACK=1,SEQ=v,ACK=u+1 的报文(v为服务端已发送数据的最后一个字节序号+1)
  • 报文含义:确认收到客户端的关闭请求,告知客户端"我已收到你的FIN,你不要再发数据了"
  • 报文特性:不携带数据则不消耗序号
  • 状态流转:
    • 服务端从ESTABLISHEDCLOSE_WAIT(关闭等待)
    • 客户端收到该ACK后,从FIN_WAIT_1FIN_WAIT_2(终止等待2)
  • 关键说明:此时客户端→服务端的写通道关闭,但服务端→客户端的读通道仍正常,服务端可继续向客户端发送剩余数据。
第三步:服务端发送FIN终止报文(第三次挥手)
  • 服务端动作:应用层剩余数据全部发送完成,调用close(),发送FIN=1,ACK=1,SEQ=w,ACK=u+1 的报文(w为服务端在CLOSE_WAIT状态发送数据后的最终序号)
  • 报文含义:服务端无数据要发送,请求关闭服务端→客户端的写通道
  • 报文特性:消耗1个序号
  • 状态流转:服务端从CLOSE_WAITLAST_ACK(最后确认),等待客户端的最终ACK。
第四步:客户端回复ACK确认报文(第四次挥手)
  • 客户端动作:收到服务端FIN报文后,发送ACK=1,SEQ=u+1,ACK=w+1 的报文
  • 报文含义:确认收到服务端的FIN,告知服务端"我已收到你的关闭请求,你可以关闭了"
  • 状态流转:
    • 服务端收到该ACK后,从LAST_ACKCLOSED,连接完全关闭
    • 客户端发送后,从FIN_WAIT_2TIME_WAIT(时间等待),必须等待2MSL 时长后,才进入CLOSED状态。

3.3 核心补充:TIME_WAIT状态详解

TIME_WAIT是主动关闭方必经的状态,Linux默认时长为30秒(RFC1122建议2分钟),等于2倍MSL(Maximum Segment Lifetime,报文最大生存时间,指IP报文在互联网中能存活的最长时间)。

为什么必须要有TIME_WAIT?
  1. 确保被动关闭方能正常关闭连接

    若客户端最后一个ACK报文丢失,服务端会超时重传FIN报文。客户端在TIME_WAIT状态可接收重传的FIN,并重传ACK、重启2MSL计时器。若无TIME_WAIT,客户端直接关闭,服务端收不到ACK,会持续重传FIN,最终发送RST强制关闭,导致服务端报错。

  2. 防止本次连接的历史报文干扰新连接

    2MSL的时长,足以让本次连接的所有残留报文在网络中完全过期消失,避免延迟的历史报文,在后续复用端口的新连接中被错误接收。

TIME_WAIT优化方案
  • 安全方案:开启tcp_timestamps(时间戳)+ tcp_tw_reuse(允许将TIME_WAIT端口用于新的出站连接)
  • 限流方案:调整tcp_max_tw_buckets,限制系统中TIME_WAIT的最大数量
  • 禁用方案:不建议修改MSL时长,tcp_tw_recycle在NAT环境下有严重问题,Linux 4.12后已移除该参数。

五、核心问题:为什么握手是三次、挥手是四次?

4.1 为什么握手必须是三次,不能是两次/四次?

1. 为什么不能是两次握手?

两次握手的核心缺陷,是无法解决双向确认失效无效连接资源浪费两大问题,也是RFC793明确禁止两次握手的核心原因:

  • 缺陷1:无法完成全双工的序号同步与能力确认

    TCP是全双工协议,需要双方都确认对方的序号有效、收发能力正常。两次握手仅能让客户端确认服务端的收发能力,服务端无法确认客户端的接收能力(服务端发送的SYN+ACK可能丢失,客户端未收到,服务端却认为连接已建立,一直空等数据,浪费资源)。

  • 缺陷2:无法处理失效的SYN报文,导致服务端资源耗尽

    典型场景:客户端发送的SYN报文因网络延迟滞留,客户端超时重发SYN并完成连接、传输数据、关闭连接后,滞留的失效SYN才到达服务端。

    若为两次握手,服务端收到失效SYN后会直接建立连接,发送SYN+ACK,而客户端已关闭连接,不会响应,服务端会一直维持这个无效连接,大量此类场景会导致服务端资源耗尽,引发拒绝服务。

    三次握手时,服务端收到失效SYN后回复SYN+ACK,客户端不会回ACK,服务端收不到ACK则不会建立连接,从根源上避免了无效资源占用。

2. 为什么不需要四次握手?

三次握手已经完成了全双工连接所需的双向同步+双向确认的全部动作:

  • 客户端SYN:同步客户端ISN
  • 服务端SYN+ACK:同步服务端ISN + 确认客户端ISN
  • 客户端ACK:确认服务端ISN

服务端的SYN和ACK两个动作无依赖关系,可合并在同一个报文中发送,无需拆分两次。四次握手属于冗余操作,不会提升可靠性,只会增加连接建立的延迟。

4.2 为什么挥手必须是四次,不能是三次?

核心原因:TCP全双工通道的读写分离特性,以及被动关闭方的残留数据传输需求,导致ACK和FIN无法合并发送,必须拆分两次。

握手与挥手的核心差异
场景 报文合并可行性 核心原因
三次握手 服务端SYN+ACK可合并 服务端收到客户端SYN时,无需等待应用层处理,可直接同步自身ISN并回复确认,两个动作无依赖
四次挥手 被动关闭方ACK+FIN不可合并 被动关闭方收到主动关闭方的FIN时,仅代表对方不再发数据,但自身可能还有大量未发送完的应用数据,必须先回复ACK确认关闭请求,等待应用层把所有数据发送完成后,才能发送FIN报文关闭自身写通道。因此ACK和FIN必须分两次发送,形成四次挥手。
特殊情况:三次挥手

当被动关闭方收到FIN时,已经没有任何残留数据要发送,内核会将ACK和FIN合并在同一个报文中发送,此时挥手流程简化为三次。这是Linux内核支持的合法场景,但属于特例,并非TCP标准的通用流程。


六、关键细节与高频易错点

5.1 半连接队列与全连接队列

  • 半连接队列(SYN队列):服务端收到SYN后,将SYN_RCVD状态的连接放入该队列,等待客户端的第三次握手ACK
  • 全连接队列(ACCEPT队列):服务端收到第三次握手ACK后,将ESTABLISHED状态的连接放入该队列,等待应用层调用accept()取走
  • 风险点:SYN洪水攻击------攻击者伪造大量源IP发送SYN报文,不回复ACK,导致半连接队列被占满,正常连接无法建立。防护方案:SYN Cookie、调整队列大小、SYN重传保护、SYN速率限制。

5.2 close()与shutdown()的核心区别

操作 通道关闭范围 引用计数影响 核心场景
close() 同时关闭读写双方向通道 socket引用计数减1,计数为0时才发送FIN 完全关闭连接,多进程共享socket时,仅最后一个close会触发FIN
shutdown() 可选择仅关闭写通道(SHUT_WR),读通道保持正常 无视引用计数,直接发送FIN报文 半关闭场景:主动关闭方不再发数据,但仍要接收对方的剩余数据,是四次挥手的标准使用场景

5.3 RST复位报文的核心使用场景

RST报文用于异常中断连接,无需经过四次挥手流程,常见场景:

  • 目标端口未打开,收到SYN连接请求,回复RST
  • 收到不存在的TCP连接的报文,回复RST
  • 连接超时,发送RST中断连接
  • 异常关闭连接,跳过TIME_WAIT状态
  • 半关闭状态下,已关闭写通道的一方收到对方的数据,回复RST。

七、拓展延伸与实战场景

  1. Wireshark抓包验证

    • 三次握手抓包特征:连续3个报文,标志位依次为[SYN][SYN, ACK][ACK]
    • 四次挥手抓包特征:连续4个报文,标志位依次为[FIN, ACK][ACK][FIN, ACK][ACK]
  2. HTTP协议中的连接管理

    • HTTP/1.0:默认短连接,每次请求都要三次握手+四次挥手,性能极差
    • HTTP/1.1:默认长连接(Connection: keep-alive),多个请求复用一个TCP连接,空闲超时后才关闭
    • HTTP/2:多路复用,单TCP连接并行处理多个请求,进一步降低握手挥手开销
    • HTTP/3:基于QUIC协议(UDP封装),无TCP的三次握手四次挥手,支持0-RTT/1-RTT快速建立连接,彻底解决TCP队头阻塞问题。
  3. 异常场景处理

    • 第三次握手ACK丢失:服务端会超时重传SYN+ACK(默认5次),若客户端已进入ESTABLISHED状态,发送数据时会携带ACK,服务端收到后会自动进入ESTABLISHED状态
    • 第四次挥手ACK丢失:服务端会超时重传FIN,客户端在TIME_WAIT状态收到后会重传ACK,重启2MSL计时器;若多次重传失败,服务端会发送RST强制关闭连接。
相关推荐
小吴编程之路2 小时前
TCP 通信中的四种核心异常情况
服务器·网络·tcp/ip
闻哥2 小时前
MySQL索引核心原理:B+树生成、页分裂与页合并全解析
java·jvm·b树·mysql·adb·面试·springboot
蜡台2 小时前
Android Gradle 项目下载编译失败解决---持续更新
android·java·kotlin·gradle
迈巴赫车主2 小时前
天梯赛 L2-004 这是二叉搜索树吗?java
java·开发语言·数据结构·算法·天梯赛
nanaki502132 小时前
LWIP----ethernet硬件设计
网络·lwip
JMchen1232 小时前
跨技术栈:在Flutter/Compose中应用自定义View思想
java·经验分享·flutter·canvas·dart·自定义view
黄昏晓x2 小时前
C++11
android·java·c++
keep intensify2 小时前
康复训练 6
网络
Java水解2 小时前
RUST异步并发安全与内存管理的最佳实践
java·后端·面试