文章目录
- TCP三次握手与四次挥手
-
- 一、前置基础:TCP核心特性与报文核心字段
-
- [1.1 TCP的核心特性](#1.1 TCP的核心特性)
- [1.2 TCP报文核心头部字段(握手/挥手相关)](#1.2 TCP报文核心头部字段(握手/挥手相关))
- 二、TCP三次握手/四次挥手(对比表格)
-
- [1、TCP三次握手全流程 结构化表格](#1、TCP三次握手全流程 结构化表格)
- [2、TCP四次挥手全流程 结构化表格](#2、TCP四次挥手全流程 结构化表格)
- [3、三次握手与四次挥手 核心差异对比表](#3、三次握手与四次挥手 核心差异对比表)
- 三、TCP三次握手全流程
-
- [2.1 前提状态](#2.1 前提状态)
- [2.2 分步流程(含状态流转)](#2.2 分步流程(含状态流转))
- [2.3 关键补充:初始序号ISN为什么是随机的?](#2.3 关键补充:初始序号ISN为什么是随机的?)
- 四、TCP四次挥手全流程
-
- [3.1 前提状态](#3.1 前提状态)
- [3.2 分步流程(含状态流转)](#3.2 分步流程(含状态流转))
- [3.3 核心补充:TIME_WAIT状态详解](#3.3 核心补充:TIME_WAIT状态详解)
- 五、核心问题:为什么握手是三次、挥手是四次?
-
- [4.1 为什么握手必须是三次,不能是两次/四次?](#4.1 为什么握手必须是三次,不能是两次/四次?)
-
- [1. 为什么不能是两次握手?](#1. 为什么不能是两次握手?)
- [2. 为什么不需要四次握手?](#2. 为什么不需要四次握手?)
- [4.2 为什么挥手必须是四次,不能是三次?](#4.2 为什么挥手必须是四次,不能是三次?)
- 六、关键细节与高频易错点
-
- [5.1 半连接队列与全连接队列](#5.1 半连接队列与全连接队列)
- [5.2 close()与shutdown()的核心区别](#5.2 close()与shutdown()的核心区别)
- [5.3 RST复位报文的核心使用场景](#5.3 RST复位报文的核心使用场景)
- 七、拓展延伸与实战场景
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个:
- 同步双方的初始序号ISN,为可靠字节流传输奠定编号基础
- 协商TCP核心传输选项(MSS、窗口缩放、SACK等)
- 确认双方的发送能力、接收能力均正常,完成双向能力校验
2.1 前提状态
- 服务端:调用
listen()系统调用,从CLOSED状态进入LISTEN(监听)状态,等待客户端连接请求 - 客户端:处于
CLOSED状态,准备发起连接
2.2 分步流程(含状态流转)
第一步:客户端发送SYN同步报文(第一次握手)
- 客户端动作:调用
connect(),随机生成自身初始序号ISN(c),发送SYN=1,SEQ=ISN© 的报文 - 报文特性:不携带应用数据,消耗1个序号
- 状态流转:客户端从
CLOSED→SYN_SENT(同步已发送)
第二步:服务端回复SYN+ACK报文(第二次握手)
- 服务端动作:收到客户端SYN报文后,完成两个核心动作:
- 确认客户端的SYN:置
ACK=1,确认号ACK=ISN(c)+1 - 同步自身初始序号:随机生成
ISN(s),置SYN=1,SEQ=ISN(s)
- 确认客户端的SYN:置
- 报文特性:不携带应用数据,消耗1个序号
- 状态流转:服务端从
LISTEN→SYN_RCVD(同步已接收)
第三步:客户端回复ACK确认报文(第三次握手)
- 客户端动作:收到服务端SYN+ACK报文后,发送ACK=1,SEQ=ISN©+1,ACK=ISN(s)+1 的报文
- 报文特性:可携带应用数据;若不携带数据,不消耗序号
- 状态流转:
- 客户端发送后,从
SYN_SENT→ESTABLISHED(连接已建立) - 服务端收到该ACK后,从
SYN_RCVD→ESTABLISHED
- 客户端发送后,从
- 结果:全双工TCP连接正式建立,双方可开始双向数据传输。
2.3 关键补充:初始序号ISN为什么是随机的?
RFC793规定ISN为基于时钟的递增计数器(每4微秒+1,约4.55小时循环一次),而非固定值,核心原因:
- 防止历史延迟报文被错误接收:避免上一次连接的残留报文,在新连接中被当成有效数据处理
- 提升安全性:防止TCP序号预测攻击,避免攻击者伪造合法报文干扰连接。
四、TCP四次挥手全流程
四次挥手的本质是全双工连接的双向安全关闭,核心目标:确保双方所有数据都传输完成,无数据丢失,同时安全释放连接资源。
核心前提:TCP全双工的读写通道独立,关闭一个方向的通道,不影响另一个方向的数据传输。
3.1 前提状态
双方均处于ESTABLISHED状态,假设客户端为主动关闭方 ,服务端为被动关闭方(任意一方均可发起主动关闭)。
3.2 分步流程(含状态流转)
第一步:客户端发送FIN终止报文(第一次挥手)
- 客户端动作:调用
close()/shutdown(),发送FIN=1,SEQ=u 的报文(u为客户端已发送数据的最后一个字节序号+1) - 报文含义:客户端无数据要发送,请求关闭客户端→服务端的写通道
- 报文特性:不携带应用数据,消耗1个序号
- 状态流转:客户端从
ESTABLISHED→FIN_WAIT_1(终止等待1)
第二步:服务端回复ACK确认报文(第二次挥手)
- 服务端动作:收到FIN报文后,发送ACK=1,SEQ=v,ACK=u+1 的报文(v为服务端已发送数据的最后一个字节序号+1)
- 报文含义:确认收到客户端的关闭请求,告知客户端"我已收到你的FIN,你不要再发数据了"
- 报文特性:不携带数据则不消耗序号
- 状态流转:
- 服务端从
ESTABLISHED→CLOSE_WAIT(关闭等待) - 客户端收到该ACK后,从
FIN_WAIT_1→FIN_WAIT_2(终止等待2)
- 服务端从
- 关键说明:此时客户端→服务端的写通道关闭,但服务端→客户端的读通道仍正常,服务端可继续向客户端发送剩余数据。
第三步:服务端发送FIN终止报文(第三次挥手)
- 服务端动作:应用层剩余数据全部发送完成,调用
close(),发送FIN=1,ACK=1,SEQ=w,ACK=u+1 的报文(w为服务端在CLOSE_WAIT状态发送数据后的最终序号) - 报文含义:服务端无数据要发送,请求关闭服务端→客户端的写通道
- 报文特性:消耗1个序号
- 状态流转:服务端从
CLOSE_WAIT→LAST_ACK(最后确认),等待客户端的最终ACK。
第四步:客户端回复ACK确认报文(第四次挥手)
- 客户端动作:收到服务端FIN报文后,发送ACK=1,SEQ=u+1,ACK=w+1 的报文
- 报文含义:确认收到服务端的FIN,告知服务端"我已收到你的关闭请求,你可以关闭了"
- 状态流转:
- 服务端收到该ACK后,从
LAST_ACK→CLOSED,连接完全关闭 - 客户端发送后,从
FIN_WAIT_2→TIME_WAIT(时间等待),必须等待2MSL 时长后,才进入CLOSED状态。
- 服务端收到该ACK后,从
3.3 核心补充:TIME_WAIT状态详解
TIME_WAIT是主动关闭方必经的状态,Linux默认时长为30秒(RFC1122建议2分钟),等于2倍MSL(Maximum Segment Lifetime,报文最大生存时间,指IP报文在互联网中能存活的最长时间)。
为什么必须要有TIME_WAIT?
-
确保被动关闭方能正常关闭连接
若客户端最后一个ACK报文丢失,服务端会超时重传FIN报文。客户端在TIME_WAIT状态可接收重传的FIN,并重传ACK、重启2MSL计时器。若无TIME_WAIT,客户端直接关闭,服务端收不到ACK,会持续重传FIN,最终发送RST强制关闭,导致服务端报错。
-
防止本次连接的历史报文干扰新连接
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。
七、拓展延伸与实战场景
-
Wireshark抓包验证
- 三次握手抓包特征:连续3个报文,标志位依次为
[SYN]、[SYN, ACK]、[ACK] - 四次挥手抓包特征:连续4个报文,标志位依次为
[FIN, ACK]、[ACK]、[FIN, ACK]、[ACK]
- 三次握手抓包特征:连续3个报文,标志位依次为
-
HTTP协议中的连接管理
- HTTP/1.0:默认短连接,每次请求都要三次握手+四次挥手,性能极差
- HTTP/1.1:默认长连接(
Connection: keep-alive),多个请求复用一个TCP连接,空闲超时后才关闭 - HTTP/2:多路复用,单TCP连接并行处理多个请求,进一步降低握手挥手开销
- HTTP/3:基于QUIC协议(UDP封装),无TCP的三次握手四次挥手,支持0-RTT/1-RTT快速建立连接,彻底解决TCP队头阻塞问题。
-
异常场景处理
- 第三次握手ACK丢失:服务端会超时重传SYN+ACK(默认5次),若客户端已进入ESTABLISHED状态,发送数据时会携带ACK,服务端收到后会自动进入ESTABLISHED状态
- 第四次挥手ACK丢失:服务端会超时重传FIN,客户端在TIME_WAIT状态收到后会重传ACK,重启2MSL计时器;若多次重传失败,服务端会发送RST强制关闭连接。