TCP 三次握手

TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在 TCP 协议中,"三次握手"(Three-way Handshake)是建立连接的重要步骤,它发生在数据传输之前。

TCP 三次握手的发展历史

TCP三次握手是在1974年由Vint Cerf和Robert Kahn提出的,最初是在RFC 675中定义的。当时,互联网还处于起步阶段,网络连接经常不稳定。为了防止旧的连接请求干扰新的连接请求,TCP使用了三次握手来建立连接。

TCP 三次握手的实现细节

TCP 三次握手的实现细节可以通过以下步骤来理解:

假设客户端选择的初始序列号 X 为 100,服务器选择的初始序列号 Y 为 300。

客户端                           服务器
  |                              |
  |  1. SYN, Seq=100             |
  |----------------------------->|
  |                              |
  |  2. SYN-ACK, Seq=300, Ack=101|
  |<-----------------------------|
  |                              |
  |  3. ACK, Seq=101, Ack=301    |
  |----------------------------->|
  |                              |
  1. 客户端发送 SYN 包到服务器,序列号为 100。

这一步的目的是让服务器知道客户端想要建立连接,并且告诉服务器客户端的初始序列号。

SYN包是TCP/IP协议中的一种控制标志,它是"同步序列编号"(Synchronize Sequence Numbers)的缩写。在TCP三次握手过程中,SYN包用于初始化一个连接。发送SYN包的一方将选择一个初始序列号,并将这个序列号放在TCP头部的序列号字段中,然后将SYN标志位设置为1,表示这是一个连接请求。

  1. 服务器收到 SYN 包后,回应 SYN-ACK 包,序列号为 300,确认号为 101。

这一步的目的是让客户端知道服务器已经准备好建立连接,并且告诉客户端服务器的初始序列号。

接收到SYN包的一方如果同意建立连接,会回应一个SYN-ACK包,同时也会选择一个初始序列号。这样,双方就可以通过这两个初始序列号来同步后续的数据传输。

  1. 客户端收到 SYN-ACK 包后,发送 ACK 包,序列号为 101,确认号为 301。

这一步的目的是让服务器知道客户端已经准备好建立连接。

通过这三次握手,客户端和服务器就可以确认彼此已经准备好建立连接,并且知道了对方的初始序列号,这样就可以开始进行可靠的数据传输了。

为什么需要三次?两次不可以吗?

TCP协议选择三次握手而不是两次握手的原因是为了防止已失效的连接请求报文段突然又传到了服务端,因而产生错误。具体来说,假设如果只有两次握手,那么就可能出现以下的情况:

客户端                           服务端
  |                              |
  |  1. SYN, 进入SYN-SENT状态     |
  |----------------------------->|
  |                              |
  |  2. 收到滞留的SYN,发送ACK      |
  |<-----------------------------|
  |                              |
  |  3. 收到错误的ACK,丢弃         |
  |                              |
  |                              |
  |  4. 服务端等待数据,形成半开连接  |
  |                              |
  1. 首先,客户端发送一个SYN包并进入SYN-SENT状态。然后,由于网络中滞留的这个复制的连接请求报文段以后又传到了服务端。

  2. 服务端收到这个滞留的连接请求报文段后,误认为是客户端再次发起的一个新的连接,于是就向客户端发送确认报文段,同意建立连接。

  3. 但是,此时客户端接收到这个确认报文段后,就认为是错误的报文段,因为此前网络滞留的缘故以为此前的请求报文失效了,因此这时客户端切换为了没有发送连接请求的状态。所以,就直接丢弃了这个报文段。

  4. 但是服务端却认为新的连接已经建立,并一直等待客户端发送数据。这样就形成了服务端一直在等待,而客户端却不知道服务端在等待的情况,也就是所谓的"半开连接",这将会浪费服务端大量的资源。

言而总之,两次握手会导致一方(此处是服务端)以为链接已经建立,但是另一方却不这样认为,二者无法达成共识,进而导致服务端盲等,即半开链接。

而三次握手可以有效防止这种情况的发生。在刚刚的例子中,如果采用三次握手,那么服务端在发送确认报文段后,需要接收到客户端的再次确认,才会认为连接已经建立。如果没有收到客户端的再次确认,服务端就会认为连接没有建立,就不会一直等待,从而避免了资源的浪费。

为什么建立连接需要三次?四次不可以吗?

TCP协议选择三次握手而不是四次握手的原因是为了提高效率和减少不必要的网络负载。在TCP协议中,三次握手已经足够确保连接的可靠性。

如果我们使用四次握手,那么第四次握手将是冗余的,因为在第三次握手后,双方已经确认了连接的建立。第四次握手将会增加网络负载,并且可能会导致连接建立的延迟。因此,TCP协议选择了三次握手而不是四次握手。

第二次握手为什么还要发送 SYN ?

在TCP三次握手过程中,第二次握手时,服务器向客户端发送的是SYN-ACK包,其中的SYN是为了告诉客户端,服务器也准备好建立连接了。

具体来说,当服务器收到客户端的SYN包(同步序列编号)后,服务器需要回应一个SYN-ACK包。这个SYN-ACK包中,ACK是对客户端SYN包的确认,而SYN则是服务器自己的连接请求。这样,客户端和服务器就可以同时确认对方的连接请求,从而建立起连接。

这个过程可以确保连接的双向性,即客户端和服务器都确认了对方的连接请求,这样才能开始数据传输。如果没有这个过程,那么连接就只能是单向的,即只有客户端确认了服务器的连接请求,但服务器并没有确认客户端的连接请求,这样就不能进行数据传输。

TCP 三次握手的状态转移

TCP 三次握手的状态转移可以通过以下的方式来表示:

客户端状态        动作/事件        服务器状态
------------------------------------------------
CLOSED        --> 发送 SYN     --> LISTEN
SYN-SENT      <-- 返回 SYN-ACK <-- SYN-RECEIVED
ESTABLISHED   --> 发送 ACK     --> ESTABLISHED

在这个图中,箭头表示状态的转移,箭头的两端是转移前后的状态,箭头上的文字是触发状态转移的事件或动作。这个过程描述了 TCP 三次握手的状态转移过程。

  1. CLOSED --> 发送 SYN --> LISTEN:在 TCP 三次握手开始时,客户端处于 CLOSED 状态,服务器处于 LISTEN 状态。客户端发送 SYN 包给服务器,请求建立连接。

  2. SYN-SENT <-- 返回 SYN-ACK <-- SYN-RECEIVED:服务器收到 SYN 包后,返回 SYN-ACK 包给客户端,并进入 SYN-RECEIVED 状态。客户端收到 SYN-ACK 包后,进入 SYN-SENT 状态。

  3. ESTABLISHED --> 发送 ACK --> ESTABLISHED:客户端发送 ACK 包给服务器,确认建立连接,然后进入 ESTABLISHED 状态。服务器收到 ACK 包后,也进入 ESTABLISHED 状态。

至此,三次握手完成,TCP 连接建立。在 ESTABLISHED 状态下,客户端和服务器可以开始数据传输。

这个过程确保了 TCP 连接的可靠性。只有当客户端和服务器都确认对方的 SYN 包和 ACK 包,才会进入 ESTABLISHED 状态,开始数据传输。这就是为什么 TCP 被称为是一种可靠的传输协议。

总结

TCP三次握手是建立TCP连接的重要步骤,它包括客户端发送SYN包,服务器回应SYN-ACK包,以及客户端发送ACK包这三个步骤。这个过程确保了TCP连接的可靠性,只有当客户端和服务器都确认对方的SYN包和ACK包,才会开始数据传输。此外,三次握手还能防止已失效的连接请求报文段突然又传到了服务端,因而产生错误。