跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

TCP 三次握手与四次挥手详解

TCP 三次握手用于建立连接,同步初始序列号并确认双方收发能力,防止失效连接请求导致资源浪费。四次挥手用于关闭连接,因 TCP 全双工特性需分别关闭发送通道。主动关闭方进入 TIME-WAIT 状态等待 2MSL,确保最后一个 ACK 到达服务器并使网络中旧报文消失。文章详细解析了各阶段标志位、状态机变化及设计原理。

GitMaster发布于 2026/2/24更新于 2026/7/2040 浏览
TCP 三次握手与四次挥手详解

核心概念与背景

在开始之前,先明确几个关键点:

  1. TCP 是什么:传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
  2. '连接'是什么:TCP 的连接并非物理上的电路,而是通信双方在内存中维护的一种状态信息(如序列号、窗口大小等)。建立连接就是同步双方的初始状态。
  3. 标志位:TCP 头部中有几个重要的控制位,在握手挥手中起到关键作用:
    • SYN:同步序列号,用于建立连接。
    • ACK:确认,表示确认号字段有效。
    • FIN:终止,用于关闭连接。
  4. 序列号与确认号:
    • 序列号:标识发送的数据字节流的顺序。
    • 确认号:期望收到对方下一个报文段的第一个数据字节的序列号,表示此序号之前的数据已成功接收。

三次握手 — 建立连接

三次握手的根本目的是:同步双方的初始序列号,并交换其他参数,为可靠数据传输做准备。同时,它也证明了双方都具有发送和接收的能力。

假设客户端(Client)主动发起连接,服务器(Server)等待连接。

第一步:第一次握手(Client -> Server)

  • 客户端发送一个 TCP 报文段。
  • 设置标志位 SYN = 1,表示这是连接请求。
  • 同时,客户端会随机生成一个初始序列号 client_isn,放在序列号字段中。
  • 此时不携带应用层数据。
  • 客户端状态:从 CLOSED 进入 SYN-SENT(同步已发送)。

第二步:第二次握手(Server -> Client)

  • 服务器收到 SYN 报文后,如果同意建立连接,则会回复一个报文段。
  • 设置标志位 SYN = 1 和 ACK = 1。
  • 服务器也会随机生成自己的初始序列号 server_isn,放在序列号字段中。
  • 确认号字段设置为 client_isn + 1,表示'我已收到你的序列号为 client_isn 的 SYN 包,期待下一个数据从 client_isn + 1 开始'。
  • 此时可以携带或不携带应用层数据(但通常不携带)。
  • 服务器状态:从 LISTEN 进入 SYN-RCVD(同步已收到)。

第三步:第三次握手(Client -> Server)

  • 客户端收到服务器的 SYN-ACK 报文后,会再发送一个确认报文。
  • 设置标志位 ACK = 1。
  • 序列号字段设置为 client_isn + 1(因为第一次握手消耗了一个序列号)。
  • 确认号字段设置为 server_isn + 1,表示'我已收到你的序列号为 server_isn 的 SYN 包'。
  • 此报文可以携带应用层数据(连接建立后即可开始传输)。
  • 客户端状态:从 SYN-SENT 进入 ESTABLISHED(已建立连接)。
  • 服务器收到这个 ACK 后,状态也从 SYN-RCVD 进入 ESTABLISHED。

为什么是三次,而不是两次?

  • 根本原因:防止已失效的连接请求报文突然到达,导致资源浪费和错误。
  • 经典场景:客户端发送了一个 SYN 报文,但由于网络拥堵,迟迟未到达服务器。客户端超时重传了一个新的 SYN 并成功建立了连接、传输数据、关闭连接。此时,那个失效的旧 SYN 报文终于到达了服务器。如果只用两次握手,服务器会认为这是一个新的连接请求,直接进入 ESTABLISHED 状态,并分配资源等待客户端发送数据,但客户端早已关闭,不会发送任何数据。这就造成了服务器的资源被白白占用。
  • 三次握手的作用:在两次握手的情况下,服务器会直接建立连接。而采用三次握手,服务器在收到失效的 SYN 后,会回复 SYN-ACK,但客户端(因为已经关闭)不会回复最终的 ACK,因此服务器在 SYN-RCVD 状态等待一段时间后,会因收不到 ACK 而关闭这个半连接,从而避免资源浪费。

四次挥手 — 关闭连接

四次挥手的目的是:双方都确认没有数据需要发送了,然后安全地关闭连接。由于 TCP 连接是全双工的(可以同时独立地进行双向数据传输),因此每个方向都必须单独关闭。

假设客户端主动发起关闭。

第一步:第一次挥手(Client -> Server)

  • 客户端应用程序调用 close() 方法,发送一个 TCP 报文段。
  • 设置标志位 FIN = 1。
  • 携带一个当前的序列号 seq = u。
  • 客户端状态:从 ESTABLISHED 进入 FIN-WAIT-1(终止等待 1)。

第二步:第二次挥手(Server -> Client)

  • 服务器收到 FIN 报文后,立即回复一个确认报文。
  • 设置标志位 ACK = 1。
  • 确认号设置为 u + 1。
  • 此时,从客户端到服务器方向的连接就关闭了。客户端不会再向服务器发送数据,但服务器可能还有数据要发送给客户端,连接处于半关闭状态。
  • 服务器状态:从 ESTABLISHED 进入 CLOSE-WAIT(关闭等待)。
  • 客户端状态:收到这个 ACK 后,从 FIN-WAIT-1 进入 FIN-WAIT-2(终止等待 2)。

第三步:第三次挥手(Server -> Client)

  • 当服务器将剩余数据全部发送完毕后,它的应用程序也会调用 close() 方法,发送一个 FIN 报文。
  • 设置标志位 FIN = 1(通常还会同时设置 ACK = 1)。
  • 携带一个序列号 seq = w。
  • 服务器状态:从 CLOSE-WAIT 进入 LAST-ACK(最后确认)。

第四步:第四次挥手(Client -> Server)

  • 客户端收到服务器的 FIN 报文后,必须发送一个确认报文。
  • 设置标志位 ACK = 1。
  • 确认号设置为 w + 1。
  • 客户端状态:从 FIN-WAIT-2 进入 TIME-WAIT(时间等待)。此时客户端连接尚未完全关闭,需要等待 2MSL(两倍的最大报文段生存时间)时长。
  • 服务器收到这个 ACK 后,状态从 LAST-ACK 进入 CLOSED,连接正式关闭。

为什么客户端需要 TIME-WAIT 状态?等待 2MSL 的目的是什么?

  1. 可靠地终止连接:确保客户端发送的最后一个 ACK 能到达服务器。如果这个 ACK 丢失,服务器会超时重传 FIN 报文。客户端在 TIME-WAIT 状态下可以收到这个重传的 FIN,并重发 ACK。
  2. 让旧的重复报文在网络中消逝:等待 2MSL 时间,足以让本次连接过程中产生的所有报文都在网络中消失。这样,就不会影响后续使用相同四元组(源 IP、源端口、目的 IP、目的端口)的新连接。

总结与对比

阶段目的关键标志位状态变化
三次握手建立连接,同步初始序列号SYN, ACKCLOSED -> SYN-SENT -> SYN-RCVD -> ESTABLISHED
第一次挥手客户端发起关闭(关闭己方发送通道)FINESTABLISHED -> FIN-WAIT-1
第二次挥手服务器确认收到关闭请求ACKESTABLISHED -> CLOSE-WAIT / FIN-WAIT-1 -> FIN-WAIT-2
第三次挥手服务器发起关闭(关闭己方发送通道)FIN, ACKCLOSE-WAIT -> LAST-ACK
第四次挥手客户端确认收到关闭请求ACKFIN-WAIT-2 -> TIME-WAIT -> CLOSED

面试常见问题

三次握手(建立连接)

目的:为了确认双方的发送和接收能力都正常,并同步初始序列号(ISN)。

  1. 第一次握手(SYN):客户端发送一个 SYN 的包,并随机生成一个初始序列号 X 给服务器。意思是:'我想建立连接,这是我的起始号。'
  2. 第二次握手(SYN+ACK):服务器收到后,如果同意连接,会回复一个 SYN 和 ACK 的包。它会确认客户端的序列号,同时自己也随机生成一个初始序列号 Y。意思是:'我收到你的请求了,我同意连接。这是我的起始号。'
  3. 第三次握手(ACK):客户端收到回复后,再向服务器发送一个 ACK 的包,确认服务器的序列号。意思是:'收到你的同意了,连接建立成功。' 至此,连接建立,可以开始数据传输。

四次挥手(断开连接)

目的:双方都确认数据已发送完毕,可以安全关闭连接。之所以是四次,因为 TCP 连接是全双工的,每一方向都需要独立关闭。

  1. 第一次挥手(FIN):假设客户端数据发完了,它会发送一个 FIN 的包,并携带一个序列号。意思是:'我这边数据发完了,要关闭连接了。'
  2. 第二次挥手(ACK):服务器收到 FIN 后,会立刻回复一个 ACK 的确认包。意思是:'我知道你要关了,但我可能还有数据没发完。' 此时,客户端到服务器的连接关闭,但服务器到客户端的连接依然可以继续发送数据。
  3. 第三次挥手(FIN):当服务器也把所有数据发完后,它会发送自己的 FIN 包。意思是:'我这边数据也发完了,我也要关了。'
  4. 第四次挥手(ACK):客户端收到服务器的 FIN 后,会发送最后一个 ACK 的确认包。意思是:'好的,我们都关了。' 客户端会等待一段时间(2MSL)后彻底关闭,服务器收到这个 ACK 后立即关闭。连接完全断开。

所以总结一下,握手是三次,因为第二次的回复合并了确认和发起请求。而挥手是四次,因为服务器在收到关闭请求后,可能还有数据要发送,所以把确认和发起自己的关闭请求分成了两步,没办法合并。

为什么握手是三次,不是两次? 主要是为了防止已失效的连接请求报文突然又传到服务器,导致服务器错误地打开连接。三次握手机制下,这个失效的报文会让客户端收到一个它并未请求的确认,客户端会拒绝,服务器收不到第三次 ACK,就不会建立连接。

为什么挥手需要四次? 因为 TCP 是全双工的。当一方说'我要关了'(发 FIN),只代表它不再发送数据,但还可以接收数据。另一方可能还有数据要发送,所以必须先立刻回复一个 ACK,等自己数据发完再发 FIN。因此 ACK 和 FIN 在多数情况下不能像握手那样合并发送。

TIME_WAIT 状态是什么?为什么需要等待 2MSL? 主动关闭的一方(先发 FIN 的,比如刚才例子里的客户端)在发送完最后一个 ACK 后,会进入 TIME_WAIT 状态,等待 2 倍的最大报文段生存时间(2MSL)。 两个目的:

  1. 确保最后的 ACK 能到达:如果这个 ACK 丢失了,对方会超时重发 FIN,TIME_WAIT 状态下的客户端还能重发 ACK。
  2. 让本次连接产生的所有报文在网络中都消失,避免影响后续新建的相同端口的连接。

目录

  1. 核心概念与背景
  2. 三次握手 — 建立连接
  3. 四次挥手 — 关闭连接
  4. 总结与对比
  5. 面试常见问题
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • QClaw 上手指南:本地 AI 代理工具深度体验
  • 安卓开发面试复盘:6 次挂面后的经验总结与避坑指南
  • Python 开发 MongoDB 数据库 MCP Server 实战
  • 大语言模型框架-Megatron-LM 源码分析
  • 基于 Docker 的 Web 应用一键打包为 Android APK 实践
  • OpenClaw 入门指南:安装、配置与自动化实战
  • 前端实现 HTML 转 Word 文档的完整方案
  • 医疗咨询机器人安全加固:Qwen3Guard-Gen-WEB 实践
  • 营销团队 AIGC 内容流水线搭建实录:Meixiong Niannian 画图引擎企业级实践
  • LangChain 实战:工具调用与结构化输出
  • 网络安全入门学习指南
  • 百度发布国内首个产业级医疗大模型灵医
  • OpenClaw 部署与实战:AI 代理实现股票盯盘与策略分析
  • Kotaemon:基于 RAG 技术的多语言模型问答系统
  • WhisperLive:实时语音转文字解决方案
  • AI 驱动数据库操作:核心场景与实战指南
  • 网络安全转行两年经验:如何看待行业饱和与职业发展
  • Python 2 与 Python 3 的主要区别详解
  • 基于 Vue 和 Java 的篮球馆场地预约系统设计
  • 基于 AI 智能体的 C 语言与前端实训项目快速实现指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online