一、前言:为什么需要"握手"和"挥手"?
TCP 是面向连接、可靠的协议。在传输数据之前,双方必须先建立一条稳定的连接通道;数据传输完毕后,则需要优雅地关闭连接,以释放系统资源。这就是"握手"和"挥手"的目的。
在深入细节之前,我们先记住两个关键指针:
- **SEQ (Sequence Number, 序列号):**用来标识发送的数据字节流,解决网络包乱序问题。
- **ACK (Acknowledgment Number, 确认号):**告诉对方下一次期望收到的序列号,确认此前的数据已收到,解决丢包问题。
标志位:
- **SYN:**用于建立连接(Synchronize)。
- **ACK:**确认标识,确认收到数据。
- **FIN:**用于断开连接(Finish)。
二、TCP 三次握手 (Three-way Handshake)
三次握手是连接建立的过程,其核心目标是:双方确认自己与对方的发送和接收能力都正常,并同步初始序列号 (ISN)。
详细过程:
1、第一次握手 (SYN)
- 客户端 -> 服务器:发送一个 TCP 数据包。
- 设置 SYN = 1,表示请求建立连接。
- 选择一个初始序列号 SEQ = x (客户端 ISN)。
- 客户端状态:SYN-SENT (同步已发送)。
- 意图:"服务器,我想和你建立连接。我的初始序列号是 x。"
2、第二次握手 (SYN + ACK)
- 服务器 -> 客户端:收到 SYN 包后,回应一个数据包。
- 设置 SYN = 1 和 ACK = 1。
- 选择自己的初始序列号 SEQ = y (服务器 ISN)。
- 将确认号设置为 ACK = x + 1,表示已经收到客户端的 SYN。
- 服务器状态:SYN-RCVD (同步已收到)。
- 意图:"我同意建立连接。我的初始序列号是 y。我确认收到了你的序列号 x。"
3、第三次握手 (ACK)
- 客户端 -> 服务器:收到服务器的 SYN-ACK 包后,再次回应。
- 设置 ACK = 1。
- 序列号 SEQ = x + 1 (因为第一次握手的 SYN 包消耗了一个序列号)。
- 确认号 ACK = y + 1,表示已经收到服务器的 SYN。
- 客户端 & 服务器状态:ESTABLISHED (连接已建立)。
- 意图:"我确认收到了你的序列号 y。现在我们可以开始传输数据了!"
🤔 为什么是三次?两次不行吗?
关键在于第二次握手后,服务器并不知道客户端的接收能力是否正常。
如果只有两次握手:
- 客户端发送一个 SYN 请求,但这个包在网络中滞留了(网络拥堵)。
- 客户端超时重传了一个新的 SYN 请求,并与服务器正常完成了两次握手,传输数据后关闭了连接。
- 此时,那个滞留在网络中的旧 SYN 请求终于到达了服务器。
- 服务器会认为这是一个新的连接请求,于是回应 SYN-ACK,并进入 SYN-RCVD 状态等待客户端的 ACK。
**问题来了:**客户端早已关闭,根本不会理会这个回应。服务器将一直等待下去,浪费了宝贵的连接资源。
第三次握手的作用就是防止这种已失效的连接请求报文突然又传送到服务器,从而产生错误。它是对服务器连接请求的最终确认。只有客户端再次回应了,服务器才能确保连接是双方自愿且有效的,从而进入 ESTABLISHED 状态。
三、TCP 四次挥手 (Four-way Wavehand)
四次挥手是连接断开的过程。TCP 连接是全双工的,即数据可以同时在两个方向上传输。因此,每个方向都必须单独进行关闭。
详细过程:
1、第一次挥手 (FIN)
- 主动关闭方 (通常是客户端) -> 被动关闭方:发送一个 FIN 包。
- 设置 FIN = 1,序列号 SEQ = u。
- 主动方状态:FIN-WAIT-1 (终止等待1)。
- 意图:"我没有数据要发送了,我想关闭连接。"
2、第二次挥手 (ACK)
- 被动关闭方 -> 主动关闭方:收到 FIN 包后,回应一个 ACK 包。
- 设置 ACK = 1,确认号 ACK = u + 1。
- 被动方状态:CLOSE-WAIT (关闭等待)。
- 意图:"我知道你要关闭了,我同意。"
- 此时状态:TCP 连接处于半关闭状态。即主动方已经不能再发送数据,但被动方可能还有数据要发送,主动方仍然必须接收。
3、第三次挥手 (FIN)
- 被动关闭方 -> 主动关闭方:当被动方也没有数据要发送时,它会发送自己的 FIN 包。
- 设置 FIN = 1,序列号 SEQ = v (v 是另一方独立的序列号)。
- 被动方状态:LAST-ACK (最后确认)。
- 意图:"我也发送完了,我也要关闭了。"
4、第四次挥手 (ACK)
- 主动关闭方 -> 被动关闭方:收到 FIN 包后,回应最后一个 ACK 包。
- 设置 ACK = 1,确认号 ACK = v + 1。
- 主动方状态:进入 TIME-WAIT (时间等待) 状态。等待 2MSL (Maximum Segment Lifetime, 报文最大生存时间,通常为 2 分钟) 后,状态变为 CLOSED,彻底释放资源。
- 被动方状态:收到最后一个 ACK 后,立即变为 CLOSED 状态。
- 意图:"好的,再见。"
🤔 为什么是四次?挥手为什么需要 TIME-WAIT 状态?
为什么是四次?
因为 TCP 是全双工 的,关闭需要两个独立的过程。第二次和第三次挥手不能合并,是因为在第二次挥手(ACK)之后,被动方可能还有数据需要发送(CLOSE-WAIT 阶段),必须等所有数据都发送完毕,才能发送 FIN 包发起自己方向的关闭。
为什么需要 TIME-WAIT 状态?
这是 TCP 设计中最精妙的部分之一,主要有两个目的:
- 可靠地终止连接:确保主动方发出的最后一个 ACK 能到达被动方。如果这个 ACK 丢失,被动方会超时重传它的 FIN 包。主动方在 TIME-WAIT 状态下收到重传的 FIN 后,可以重发 ACK。
- 让旧连接报文消逝在网络中:等待足够长的时间(2MSL),确保本次连接所产生的所有报文都从网络中消失,从而防止旧连接的报文干扰新连接(和三次握手防止旧 SYN 包是同一个道理)。
四、总结与图示
三次握手

四次挥手
