# 说说 TCP 的三次握手?

7 min read
Table of Contents

说说 TCP 的三次握手?

TCP 三次握手详解

【核心定义】

TCP 三次握手是 TCP 协议在建立可靠连接时,通信双方通过交换三个特定的数据包(SYN, SYN-ACK, ACK)来同步序列号、协商参数并确认双方收发能力的过程。

【关键要点】

  1. 目的与必要性:解决信道不可靠下的初始序列号同步与参数协商问题。两次握手无法防止已失效的连接请求报文突然又传送到服务器,导致资源浪费;四次握手则冗余,三次是理论最小值。
  2. 核心流程:
    • 第一次握手 (SYN):客户端发送 SYN 包(SYN=1, seq=x)到服务器,进入 SYN_SENT 状态。
    • 第二次握手 (SYN-ACK):服务器收到 SYN 包,回复 SYN-ACK 包(SYN=1, ACK=1, seq=y, ack=x+1),进入 SYN_RCVD 状态。
    • 第三次握手 (ACK):客户端收到 SYN-ACK 包,发送 ACK 包(ACK=1, seq=x+1, ack=y+1),进入 ESTABLISHED 状态;服务器收到后也进入 ESTABLISHED 状态。
  3. 核心字段:
    • 序列号 (seq):随机生成,用于标识数据字节流顺序。
    • 确认号 (ack):期望收到的下一个字节的序列号,值为收到的 seq + 1。
    • 标志位:SYN(同步序列号)、ACK(确认有效)。

【深度推导/细节】

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

  • 两次握手的问题:无法防止历史连接初始化造成的资源占用与状态混乱。若一个滞留在网络中的旧 SYN 包到达服务器,服务器会直接建立连接并等待数据,但客户端可能早已放弃,导致服务器资源空等。
  • 三次握手的解决逻辑:
    • Step 1:客户端发送 SYN (seq=x)。(表达“我想连接,我的初始序列号是x”)
    • Step 2:服务器回应 SYN-ACK (seq=y, ack=x+1)。(表达“我同意连接,我的初始序列号是y,已确认你的x”)
    • Step 3:客户端发送 ACK (ack=y+1)。(表达“已确认你的y,连接正式建立”)
    • 关键:第三次握手携带了对服务器序列号的确认,只有服务器收到此 ACK,才能确信客户端的收发能力正常,且双方序列号已同步。若缺少此步,服务器无法区分当前连接是正常的新连接还是迟到的旧连接请求。
  • 四次握手冗余:理论上,服务器的 SYN 和 ACK 可以合并为一个包发送,因此三次已是最优。

核心数字与设计考量

  • 初始序列号 (ISN) 随机化:并非从 0 或 1 开始,而是基于时钟和算法动态生成。目的是防止序列号预测攻击(如伪造 RST 包断开连接)和避免不同连接间的报文混淆。
  • 超时与重传:若 SYN 包丢失,客户端会启动指数退避重传(如 3s, 6s, 12s…)。若服务器未收到第三次 ACK,会重传 SYN-ACK,并最终超时释放资源。

【关联/对比】

  • TCP vs UDP:TCP 是面向连接的可靠协议,需握手;UDP 是无连接的,无需握手,但不可靠。
  • TCP 四次挥手:连接释放需四次挥手,因为 TCP 支持半关闭(一方发送 FIN 后仍可接收数据),且 ACK 和 FIN 通常不能合并发送(因可能仍有数据待发送)。
  • TLS 握手:在 TCP 连接建立后,进行 TLS 握手以加密通信,两者分层协作。

『面试官追问』

  1. 两次握手会有什么问题?
    • 旧连接干扰:网络延迟导致旧 SYN 包到达,服务器误建连接,浪费资源。
    • 无法同步双方初始序列号:服务器单方面确认了客户端的序列号,但客户端未确认服务器的,可能导致后续数据确认混乱。
  2. 第三次握手丢失了怎么办?
    • 服务器未收到 ACK,会重传 SYN-ACK 包(次数和超时时间可配置,如 tcp_synack_retries)。
    • 若始终未收到,服务器超时后关闭此半连接,释放资源;客户端已进入 ESTABLISHED 状态,发送数据时会触发服务器以 RST 复位响应。
  3. SYN 洪泛攻击是什么?如何防范?
    • 攻击者伪造大量虚假 IP 发送 SYN 包,但不完成第三次握手,耗尽服务器的半连接队列资源。
    • 防范:启用 SYN Cookie(在 SYN-ACK 中编码序列号,不立即分配资源)、调整队列长度、缩短超时时间。
  4. 为什么握手是三次,挥手是四次?
    • 挥手时,被动关闭方收到 FIN 后,可能还有数据要发送,故先回复 ACK,待数据发送完毕再发送 FIN,因此 ACK 和 FIN 通常分开发送,导致四次。

【版本差异与优化】

  • TCP Fast Open (TFO):在 Linux 3.7+ 内核中支持,允许在第一次握手的 SYN 包中携带数据,减少一次 RTT,提升短连接性能(需客户端和服务器同时支持并启用)。
  • MTU 与 MSS 协商:在握手过程中,双方可通过 TCP 选项交换 MSS(最大报文段长度),以避免路径 MTU 问题导致的分片。

回答要点总结:三次握手的本质是双向的序列号同步与能力确认。三次是保证可靠且不冗余的最小次数,核心解决历史连接和资源同步问题。需清晰复述三个包的状态转换、序列号变化,并能解释其设计背后的网络攻防与性能考量。

My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

# 说说 Spring 启动过程?

6 min read

Spring 启动过程本质上是 IoC 容器的初始化过程,它通过读取配置元数据、实例化 Bean、完成依赖注入和生命周期回调,最终构建出一个可用的应用程序上下文。

Read

# 说说 Java 中 HashMap 的原理?

8 min read

HashMap 是 Java 中基于哈希表实现的、用于存储键值对(Key-Value)的非线程安全数据结构,它通过计算键的哈希值来快速定位存储位置,提供平均 O(1) 时间复杂度的查找、插入和删除操作。

Read

Comments