三次握手

为什么需要三次握手?

核心目的就一个:为了确保客户端和服务器双方都具备了可靠的发送和接收数据的能力。 这就像打电话,你不仅要确保你能听到对方,还要确保对方也能听到你,然后才能开始正常通话。

一个生动的比喻:打电话

第一次握手 (SYN)

你 (客户端) 打电话给朋友 (服务器)。你问:“喂,能听到我说话吗?”

这相当于客户端发送一个 SYN 包,意思是:“我想和你建立连接,我的初始序列号是 J。”

第二次握手 (SYN-ACK)

朋友 (服务器) 回答:“嗯,我能听到你。你能听到我吗?”

这相当于服务器回复一个 SYN 和 ACK 包,意思是:“我收到了你的请求(应答你的 J),我也想建立连接,我的初始序列号是 K。”

第三次握手 (ACK)

你 (客户端) 再次回答:“好的,我也能听到你。那我们开始聊吧!”

这相当于客户端再发送一个 ACK 包,意思是:“我收到了你的确认(应答你的 K),连接现在正式建立。”

至此,双方都确认了对方的收发能力都正常,可以开始传输数据了。

技术细节详解 我们把上面的比喻翻译成TCP协议的具体字段。

SYN (Synchronize): 一个控制位,当它为 1 时,表示请求建立一个新连接。

ACK (Acknowledge): 一个控制位,当它为 1 时,表示应答号字段有效。

序列号 (Sequence Number, Seq): 一个随机生成的32位数字。TCP是面向流的协议,它将数据看作一连串的字节,序列号就是每个字节的编号。它的主要作用是保证数据的有序性。

应答号 (Acknowledgement Number, Ack): 它的值等于期望收到的下一个字节的序列号。

第一次握手:客户端 -> 服务器 (SYN)

客户端发送一个TCP段(Segment)。

标志位: SYN = 1, ACK = 0。

序列号: Seq = J (J 是一个由客户端随机生成的初始序列号, ISN - Initial Sequence Number)。

状态变化: 客户端从 CLOSED 状态进入 SYN_SENT 状态。

含义: “你好,服务器。我想建立连接,我的起始序列号是 J。”

第二次握手:服务器 -> 客户端 (SYN-ACK)

服务器收到客户端的请求后,回复一个TCP段。

标志位: SYN = 1, ACK = 1。

序列号: Seq = K (K 是由服务器随机生成的自己的初始序列号)。

应答号: Ack = J + 1 (这个是关键!服务器告诉客户端:“我已经收到了你的序列号 J,我期望你下一个发来的数据序列号是 J+1”。+1 是因为 SYN 标志本身会消耗掉一个序列号)。

状态变化: 服务器从 LISTEN 状态进入 SYN_RCVD 状态。

含义: “好的,客户端,我收到了你的请求。我同意连接,我的起始序列号是 K,并且我确认收到了你的 J。”

第三次握手:客户端 -> 服务器 (ACK)

客户端收到服务器的确认后,再发送一个TCP段。

标志位: SYN = 0, ACK = 1 (连接已经开始建立,不再需要发送 SYN 了)。

序列号: Seq = J + 1 (客户端发送自己下一个字节的序列号)。

应答号: Ack = K + 1 (客户端告诉服务器:“我已经收到了你的序列号 K,我期望你下一个发来的数据序列号是 K+1”)。

状态变化:

客户端从 SYN_SENT 状态进入 ESTABLISHED 状态。

服务器收到这个包后,从 SYN_RCVD 状态进入 ESTABLISHED 状态。

含义: “好的,服务器,我收到了你的确认。连接正式建立!”

连接建立成功后,双方就可以在这个 ESTABLISHED 状态下相互传输应用层数据了。

为什么初始序列号(ISN)是随机的?

这是一个重要的安全机制。如果序列号是固定的或者可预测的,攻击者就很容易伪造TCP包,冒充通信的一方来中断连接或注入恶意数据,这就是所谓的“TCP序列号预测攻击”。随机化ISN使得这种攻击变得极其困难。

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

確保双方能力正常:三次握手足以让双方确认彼此的发送和接收能力都是正常的,这是可靠通信的基础。
同步序列号:确保双方的初始序列号(ISN)一致,以便后续数据传输能正确识别、跟踪和确认。
防止已失效的连接请求:如果只有两次,服务器会认为连接建立,但客户端可能已经关闭连接,造成资源浪费