什么是 TCP 三次握手、四次挥手?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

面试考察点

  1. 网络基础掌握度:面试官不仅仅是想知道你能否背诵三次握手、四次挥手的流程,更是想确认你是否理解 TCP 作为可靠传输协议的核心设计思想,以及为什么需要这样设计。

  2. 原理理解深度:考察你是否清楚每次握手/挥手的目的、状态变化、以及 "为什么是三次而不是两次"、"为什么挥手需要四次" 等深层原因。

  3. 实践关联能力:能否将理论知识与实际场景联系起来,比如连接建立失败如何排查、TIME_WAIT 状态过多怎么办等生产问题。

核心答案

TCP 三次握手是建立可靠连接的过程,客户端和服务端共交互 3 次,确保双方都具有发送和接收数据的能力。

TCP 四次挥手是断开连接的过程,需要 4 次 交互,确保双方都能优雅地关闭连接,避免数据丢失。

对比项三次握手四次挥手
目的建立可靠连接优雅断开连接
交互次数3 次4 次
核心标志位SYNACKFINACK
谁发起客户端客户端或服务端
最终状态ESTABLISHEDCLOSED

深度解析

一、TCP 三次握手详解

上图展示了 TCP 三次握手的完整流程,整体分为三个阶段:

  1. 第一次握手(Client → Server)

    • 客户端发送 SYN=1 报文,携带初始序列号 seq=x
    • 客户端进入 SYN_SENT 状态,等待服务端确认
    • 这个阶段客户端告诉服务端:"我想和你建立连接"
  2. 第二次握手(Server → Client)

    • 服务端收到 SYN 报文后,发送 SYN=1, ACK=1 报文
    • 携带服务端的初始序列号 seq=y,以及确认号 ack=x+1(确认收到客户端的 seq)
    • 服务端进入 SYN_RCVD 状态
    • 这个阶段服务端告诉客户端:"收到了你的请求,我也想和你建立连接"
  3. 第三次握手(Client → Server)

    • 客户端收到服务端的确认后,发送 ACK=1 报文
    • 携带 seq=x+1ack=y+1(确认收到服务端的 seq)
    • 双方都进入 ESTABLISHED 状态,连接建立成功
    • 这个阶段客户端告诉服务端:"收到你的确认,我们可以开始通信了"

为什么必须是三次握手?

核心原因:防止已失效的连接请求报文突然又传送到了服务端,造成资源浪费

假设采用两次握手:

三次握手的本质目的

  • 确认双方的接收能力发送能力都正常
  • 同步双方的初始序列号(ISN)
  • 防止历史连接造成资源浪费

二、TCP 四次挥手详解

上图展示了 TCP 四次挥手的完整流程,整体分为四个阶段:

  1. 第一次挥手(Client → Server)

    • 客户端发送 FIN=1 报文,表示没有数据要发送了
    • 客户端进入 FIN_WAIT_1 状态
    • 本质:客户端告诉服务端 "我说完了,准备关闭"
  2. 第二次挥手(Server → Client)

    • 服务端收到 FIN 后,发送 ACK=1 确认报文
    • 服务端进入 CLOSE_WAIT 状态,客户端进入 FIN_WAIT_2 状态
    • 此时客户端 → 服务端的通道已关闭,但服务端 → 客户端的通道还开着
    • 本质:服务端告诉客户端 "收到你的关闭请求,但我可能还有话要说"
  3. 第三次挥手(Server → Client)

    • 服务端数据发送完毕后,发送 FIN=1, ACK=1 报文
    • 服务端进入 LAST_ACK 状态
    • 本质:服务端告诉客户端 "我也说完了,同意关闭"
  4. 第四次挥手(Client → Server)

    • 客户端收到 FIN 后,发送 ACK=1 确认报文
    • 客户端进入 TIME_WAIT 状态,等待 2MSL(Maximum Segment Lifetime)
    • 服务端收到 ACK 后进入 CLOSED 状态
    • 客户端等待 2MSL 后也进入 CLOSED 状态
    • 本质:客户端告诉服务端 "确认收到,我们彻底断开吧"

为什么挥手需要四次?

核心原因:TCP 是全双工通信,每个方向的关闭需要单独确认。

三次握手中的 SYNACK 可以合并发送(SYN+ACK),但四次挥手中的 FINACK 通常不能合并,原因是:

  • 当客户端发送 FIN 时,表示客户端没有数据要发送了
  • 但服务端可能还有数据需要发送给客户端
  • 所以服务端先发送 ACK 确认收到关闭请求
  • 等服务端数据全部发送完毕后,再发送自己的 FIN

为什么客户端需要 TIME_WAIT 状态?

三、状态变迁总结

面试高频追问

  1. 为什么建立连接是三次,断开连接是四次?

    建立连接时,服务端的 SYNACK 可以合并在一个报文中发送(SYN+ACK);而断开连接时,服务端收到 FIN 后可能还有数据要发送,所以先回复 ACK,等数据发完再发送 FIN,因此需要四次。

  2. TIME_WAIT 状态过多会有什么问题?如何解决?

    问题:占用大量端口资源(客户端默认端口范围有限),导致无法建立新连接。

    解决方案

    • 开启 SO_REUSEADDR 选项,允许重用处于 TIME_WAIT 状态的端口
    • 调整 tcp_tw_reuse 参数(Linux)
    • 优化应用层连接池,减少短连接
  3. 如果第三次握手丢失会发生什么?

    服务端会超时重发 SYN+ACK(默认重试 5 次),如果一直收不到客户端的 ACK,最终服务端关闭连接,客户端触发连接超时错误。

  4. 什么是 SYN Flood 攻击?如何防范?

    攻击者发送大量伪造 IP 的 SYN 报文但不完成第三次握手,导致服务端大量连接处于 SYN_RCVD 状态,耗尽资源。

    防范措施

    • 开启 SYN Cookies 功能
    • 增加半连接队列大小
    • 缩短 SYN_RCVD 状态超时时间

常见面试变体

  • 变体一:"请描述 TCP 连接建立和断开的过程"
  • 变体二:"TCP 为什么是可靠传输?"
  • 变体三:"什么情况下会出现大量 TIME_WAIT?如何排查?"
  • 变体四:"TCP 和 UDP 的区别是什么?为什么 TCP 需要三次握手?"

记忆口诀

三次握手:一请求、二确认加请求、三确认("你听得到吗?"、"听得到,你呢?"、"我也听得到")

四次挥手:一请求关、二确认、三请求关、四确认("我说完了"、"好的等我说完"、"我也说完了"、"好的再见")

状态记忆

  • 客户端:SYN_SENTESTABLISHEDFIN_WAIT_1FIN_WAIT_2TIME_WAITCLOSED
  • 服务端:LISTENSYN_RCVDESTABLISHEDCLOSE_WAITLAST_ACKCLOSED

总结

TCP 三次握手通过 "请求 → 确认+请求 → 确认" 建立可靠连接,确保双方收发能力正常并同步初始序列号;四次挥手通过两次独立的 "请求 → 确认" 实现全双工连接的优雅关闭,TIME_WAIT 状态确保最后的 ACK 到达并消除旧数据包影响。理解这些机制有助于排查连接建立失败、连接泄漏等生产问题。