什么是三次握手?

为了准确无误地把数据发送到目标处,TCP采用了三次握手的策略。简而言之,三次握手就是用来建立连接的。

那么三次握手双方都说了什么呢? :::info A:喂,听得到吗?我的数据从序号x开始编号(SYN)
B:好的,我知道了,我的数据从序号y开始编号(SYN-ACK)
A:好的,我知道了,我们开始通话吧(ACK) :::

所以说,三次握手的过程中,不是你能不能听得到,而是协商双方开始的序号。
16d70264f4692792.jpg

  • 首先客户端发送一个SYN报文段,这个报文只有SYN被置1。另外这个报文是不携带数据的,但是它占用一个序号,意味着下次发送数据序列号要加一。客户端会随机生成一个序列号。 :::warning

    为什么SYN要占用一个序列号呢? 不占用序列号的段是不需要确认的。它里面都没内容你还确认个啥?比如ACK段。SYN段是需要对方确认的,所以要占一个序列号。

:::

  • 服务端收到客户端的SYN的报文以后,将SYN+ACK都置位,发送给客户端。SYN的作用也是同步告知服务端生成的初始序号。ACK的作用是告知客户端你发送的SYN已经收到了,并指定下一个数据段的起始序号是多少。虽然也没有携带数据,但因为SYN需要被确认,所以也要占用一个序列号。
  • 客户端最后发送一个ACK段。这个段用来确认服务端发送的SYN段。因为这个ACK段不携带任何数据,也不需要被确认,所以不占用序列号。

    为什么是3次?

    协商一个序号的过程按理说需要一个来回来完成,也就是2次。所以理论上建立连接需要2个来回(4次),互相确认双方的初始序号(Initial Sequence Number,ISN)。
    但是第二个来回的告知和上一次的确认可以合在一起发送(SYN + ACK),所以只需要三次握手,就可以建立TCP连接。
    这也解释了为什么不能只有 2 次握手:因为只能协商一个序号。

在日常应用场景中,丢包率不高,因此三次握手是最合理的做法,少了不够,多了白搭。

丢包了怎么办?

  • SYN+ACK丢包的话,发送方在等待超时后重传SYN包即可解决。

  • ACK丢包的话:

    • 对A来说,如果接着有数据要发,由于在A看起来连接已经确认,A可以立即发出下一个数据包。这个数据包中也有ACK和SN,B收到后就能成功确立连接。
    • 对B来说,如果一直没收到ACK,那么SYN+ACK这个包的计时器会超时,那么就会主动重传。知道收到A的ACK。如果重传多次后还是没有收到,就会停止尝试,并关闭TCP连接。

      三次握手的状态变化

      16b518cd1664fa5d.jpg
      对于客户端而言:
  • 初始的状态是处于 ClOSED 状态。这里的 ClOSED 不是一个真实的状态,而是一个假想的起点和终点。

  • 客户端调用connect以后会发送SYN同步报文给服务端,然后进入 SYN-SENT 状态,客户端将保持这个状态,直到它收到服务端的确认包。
  • SYN-SENT 状态下,收到了服务端的确认包。客户端将发送服务端FIN报文的确认包,,同时进入 ESTABLISHED 状态。表明自己准备好发送数据。

对于服务端而言:

  • 初始状态同样是 CLOSED 状态。
  • 在执行bind,listen之后,进入 LISTEN 状态。等待客户端的连接。
  • 收到客户端的SYN报文以后,会回复确认并发送自己的SYN报文,之后会进入 SYN-RCVD 状态等待确认。
  • 收到客户端的确认报文以后,进入 ESTABLISHED 状态,这时双方就可以互相发送数据了。