简述TCP三次握手四次挥手,为什么握手是三次,挥手是四次
1. TCP 三次握手(建立连接)
三次握手的核心目标是同步双方的初始序列号(ISN)并确认双方的收发能力都正常。可以把它想象成一个打电话确认信号的过程。
-
第一次握手 (SYN):
- 客户端想和服务器建立连接,它会向服务器发送一个特殊的 TCP 报文,其中
SYN标志位被置为 1。 - 这个报文中还包含一个客户端自己生成的随机初始序列号
seq=x。 - 此时客户端进入
SYN_SENT状态。
- 客户端想和服务器建立连接,它会向服务器发送一个特殊的 TCP 报文,其中
-
第二次握手 (SYN+ACK):
- 服务器收到客户端的
SYN报文后,如果同意连接,会回复一个报文。 - 这个报文包含两个重要的信息:
- 同意建立连接,
SYN标志位置为 1。 - 确认收到了客户端的报文,
ACK标志位置为 1。
- 同意建立连接,
- 服务器也会生成自己的初始序列号
seq=y,并用确认号ack=x+1来告知客户端“你的第一个报文我收到了”。 - 此时服务器进入
SYN_RCVD状态。
- 服务器收到客户端的
-
第三次握手 (ACK):
- 客户端收到服务器的
SYN+ACK报文后,就知道服务器的接收和发送能力都正常了。 - 客户端会再发送一个确认报文,
ACK标志位置为 1,并用确认号ack=y+1来告知服务器“你的报文我也收到了”。 - 这个报文发送完毕后,客户端和服务器都进入
ESTABLISHED状态,连接建立成功,可以开始传输数据了。
- 客户端收到服务器的
2. TCP 四次挥手(断开连接)
四次挥手的核心目标是确保双方都没有数据要发送了,然后安全地关闭连接。
-
第一次挥手 (FIN):
- 客户端(或服务器,任何一方都可以先发起)决定关闭连接,它会向对方发送一个
FIN标志位置为 1 的报文。 - 此时,发起方进入
FIN_WAIT_1状态。
- 客户端(或服务器,任何一方都可以先发起)决定关闭连接,它会向对方发送一个
-
第二次挥手 (ACK):
- 接收方收到
FIN报文后,知道对方已经不打算再发送数据了。 - 它会立即回复一个
ACK确认报文。 - 此时,接收方进入
CLOSE_WAIT状态。发起方收到这个 ACK 后,进入FIN_WAIT_2状态。这时连接处于半关闭(Half-Close)状态,即发起方不能再发送数据,但接收方如果还有数据没发完,仍然可以继续发送。
- 接收方收到
-
第三次挥手 (FIN):
- 接收方在确认自己所有的数据也都发送完毕后,会向发起方发送一个
FIN报文。 - 此时,接收方进入
LAST_ACK状态。
- 接收方在确认自己所有的数据也都发送完毕后,会向发起方发送一个
-
第四次挥手 (ACK):
- 发起方收到接收方的
FIN报文后,回复最后一个ACK确认报文。 - 发起方在发送完这个
ACK后,不会立即关闭,而是会进入TIME_WAIT状态,等待 2MSL(最大报文段生存时间)后才正式关闭。这是为了确保对方能收到这个最后的 ACK,并防止已失效的连接请求报文段出现在本连接中。 - 接收方一旦收到这个 ACK,就立即关闭连接。
- 发起方收到接收方的
3. 为什么握手是三次,而挥手是四次?
这是一个非常关键的区别,根源在于连接建立和连接断开时,服务器端的状态和需求不同。
3.1 为什么握手不能是两次?
最主要的原因是防止已失效的连接请求报文段突然又传送到了服务器,从而产生错误连接。
- 想象一个场景:客户端发送的第一个
SYN因为网络延迟,滞留了很久。客户端超时后,又重发了一个SYN,这次正常建立了连接,通信完并关闭了。 - 这时,那个早已失效的
SYN报文终于到达了服务器。如果是两次握手,服务器收到SYN后会立即建立连接并分配资源,然后等待客户端发送数据。但客户端此时根本不知道这个连接的存在,也不会发送任何数据,这就会导致服务器白白浪费资源。 - 而三次握手解决了这个问题。服务器收到那个失效的
SYN后,会回复SYN+ACK。但客户端的当前状态与这个SYN+ACK不匹配,会直接忽略或发送一个RST报文,服务器就知道这是一个无效请求,不会建立连接。 - 总结:第三次握手是为了让服务器确认“客户端也收到了我的同步信号”,从而确保这是一个真实、有效的连接请求。
3.2 为什么挥手必须是四次?
核心原因在于TCP 的半关闭(Half-Close)特性。
- TCP 是全双工通信,意味着数据可以在两个方向上独立传输。
- 当一方(比如客户端)发送
FIN说“我没数据要发了”,这仅仅代表它单方面地关闭了发送通道。 - 此时,服务器可能还有数据没有发送完。它不能在回复
ACK的同时,也立刻发送自己的FIN。它必须先用ACK(第二次挥手)告诉客户端“你的关闭请求我收到了”,然后继续处理并发送自己剩余的数据。 - 直到服务器确认自己的所有数据都发送完毕后,它才能发送自己的
FIN(第三次挥手),告诉客户端“我这边也准备好了,可以关闭了”。TCP 是可靠传输,但“最后这个 ACK”本身不再有对它的确认。 也就是说:A 发出最后 ACK如果这个 ACK 在网络里丢了 B 就会以为: “我发的 FIN 对方没确认”于是 B 会重传 FIN
如果 A 这时已经立刻把连接状态彻底删掉了,那么:
-
A 收到这个重传的 FIN
-
却发现“这连接我都没了”
-
那就没法按原连接语义正确处理了
所以 A 必须保留一段时间,万一 B 重传 FIN,A 还能再回一次 ACK。
- 总结:挥手过程中的 ACK 和 FIN 是分开的,因为服务器在收到对方的关闭请求后,可能还需要发送数据,所以不能将确认和关闭请求合并在一个报文中。这就比握手多出了一次交互。
简单来说:
- 握手时,服务器的
ACK和SYN可以合并为一次发送,因为服务器在确认对方的同时,也需要立刻同步自己的序列号,这是个原子性的需求。 - 挥手时,服务器的
ACK和FIN必须分开,因为确认对方关闭和自己准备好关闭是两个独立的事件,中间可能隔着数据传输。