一、为什么是三次握手?而不是两次呢?

有三个原因:

1)第一个原因也是主要原因:防止旧的重复连接建立造成混乱。网络拥堵的情况下,客户端可能发送多次连接请求,而旧的连接请求可能先到达服务端,这时如果是两次握手,服务端返回 ACK 报文就建立了连接,等到新的连接请求到达后,此时旧连接已经建立,所以会造成混乱;三次握手就可以避免这种情况,当服务端返回 ACK 报文时,客户端发现 ACKnum 与预期不符,就会发送 RST(Reset) 报文给服务端表示重置连接,服务端回退到 LISTEN 状态;

接收方收到 RST 报文时将采取以下三种措施:

  1. 接收方处于非同步状态SYN_SENT, SYN-RECEIVED),则返回到 LISTEN 状态;
  2. 接收方处于同步状态ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT),则终止连接,回到 CLOSED 状态;

2)为了保证双方都具有接收和发送能力:如果只有两次握手,那么服务端不知道自己的发送能力和客户端的接收能力是否正常;如果三次握手,客户端返回确认消息,服务端收到消息,就可以确认自己的发送能力和客户端的接受能力正常;

3)防止已经失效的连接请求到达服务端导致建立连接浪费资源:当客户端发送的 SYN 报文被阻塞,客户端又重发了 SYN 报文,成功与服务端建立连接,然后传输完数据后关闭了连接,此后失效的 SYN 报文到达服务端,如果是两次握手,会再次建立一个连接,服务端会一直等待客户端发送数据从而浪费了资源。

二、 为什么建立连接握手三次,关闭连接时需要是四次呢?

因为建立连接的时候服务端发送的报文中同时设置了 SYN 和 ACK 两个控制位,所以减少了一次报文的发送;而关闭连接时,主动关闭方发送控制位 FIN 置 1 的报文,表示数据已经发送完毕,可以关闭连接,但此时接收方可能还在发送数据,不能立刻关闭数据传输通道,所以不能把 FIN 包和 ACK 包同时发送,接收方先发送 ACK 包确认,然后等待应用进程发来通知说数据发送完毕了,再发送 FIN 包。

三、 为什么 TIME_WAIT 状态需要经过 2MSL 才能返回到 CLOSED 状态?

MSL(maximum segment lifetime):TCP 定义 MSL 为 120s,同时允许修改这个值;

两个原因:

1)保证客户端发送的最后一个 ACK 包能到达服务端,当这个包丢失时,服务端会再次发送 FIN 包,所以 2MSL=ACK 包到达服务端 + 服务端重发 FIN 包,接着客户端发送 ACK 包,并重启 2MSL 计时器;

2)保证关闭连接前,本次连接中产生的所有数据报文都从网络中消失,防止与新的连接混淆。

四、 为什么 FIN 和 SYN 要额外消耗一个序号?

因为 FIN 包和 SYN 包都需要确认,如果不占用序号的话,就无法辨别 ACK 包确认的是哪一个报文。

五、 双方同时发送请求建立 TCP 连接,会发生什么?

      TCP A                                            TCP B

  1.  CLOSED                                           CLOSED

  2.  SYN-SENT     --> <SEQ=100><CTL=SYN>              ...

  3.  SYN-RECEIVED <-- <SEQ=300><CTL=SYN>              <-- SYN-SENT

  4.               ... <SEQ=100><CTL=SYN>              --> SYN-RECEIVED

  5.  SYN-RECEIVED --> <SEQ=100><ACK=301><CTL=SYN,ACK> ...

  6.  ESTABLISHED  <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED

  7.               ... <SEQ=101><ACK=301><CTL=ACK>     --> ESTABLISHED

                Simultaneous Connection Synchronization

首先解释一下图中符号的含义:

  • 右箭头 (-->) :从 A 发送到 B 的 TCP 报文段,或者 B 接收到了 A 的报文;
  • 左箭头 (<--) :从 B 发送到 A 的 TCP 报文段,或者 A 接收到了 B 的报文;
  • 省略号 (…) :TCP 报文段仍滞留在网络中(delayed);
  • 丢失 (XXX) :TCP 报文段丢失或者被拒绝;
  • 注释会放在括号中;
  • TCP 状态代表了处于中间的报文段发出或者到达之后的状态(AFTER);

先说结论:双方同时发送请求建立 TCP 连接,最终只会建立一个 TCP 连接

接着我们逐行来分析这个连接过程发生了什么:

  1. 双方都处于 CLOSED 状态;
  2. 此时 A 发送 SYN 报文请求建立 TCP 连接;
  3. A 发送的连接报文还未到达 B 时,B 也发送一个报文请求建立连接,A 收到后就进入SYN-RECEIVED 状态;
  4. A 发送的连接报文到达 B,B 也进入 SYN-RECEIVED 状态;
  5. A 发送 SYN+ACK 报文,表示请求建立连接同时确认应答 B 发送的 SYN 报文,注意,这里的序列号 SEQ 是初始序列号,所以与第 1 次发送的序列号一致;
  6. B 也同时发送 SYN+ACK 报文,A 收到后就进入 ESTABLISHED 状态,表示成功建立连接;
  7. 最后 A 发送的 SYN+ACK 报文到达 B 端,我们注意到,第 7 行 B 收到的报文与第 5 行 A 发送的报文不一样,这是为什么呢?这是因为前面 B 已经收到 A 发送的 SEQ=100 的报文了(见第 4 行),所以实际上 B 只接收 A 发送的从序列号 101 开始的 ACK 报文,这就是第 7 行和第 5 行的报文不一样的原因。B 收到报文后也进入 ESTABLISHED 状态,最终只建立了一个 TCP 连接。
 5.  SYN-RECEIVED --> <SEQ=100><ACK=301><CTL=SYN,ACK> ...
 
 7.               ... <SEQ=101><ACK=301><CTL=ACK>     --> ESTABLISHED

六、 在建立 TCP 连接时,一个旧的 SYN 报文到达服务端,会发生什么?

      TCP A                                                TCP B

  1.  CLOSED                                               LISTEN

  2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               ...

  3.  (duplicate) ... <SEQ=90><CTL=SYN>               --> SYN-RECEIVED

  4.  SYN-SENT    <-- <SEQ=300><ACK=91><CTL=SYN,ACK>  <-- SYN-RECEIVED

  5.  SYN-SENT    --> <SEQ=91><CTL=RST>               --> LISTEN


  6.              ... <SEQ=100><CTL=SYN>               --> SYN-RECEIVED

  7.  SYN-SENT    <-- <SEQ=400><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED

  8.  ESTABLISHED --> <SEQ=101><ACK=401><CTL=ACK>      --> ESTABLISHED

                    Recovery from Old Duplicate SYN

当服务端 B 处于 SYN-RECEIVED 状态时,来自客户端 A 的一个旧 SYN 报文到达:

  1. 服务端无法判断这个报文是否是旧的,所以正常返回应答报文;
  2. 客户端收到 ACK 报文后发现 ACKnum(应答号) 不正确,所以返回一个 RST 报文表示重置;
  3. 服务器收到 RST 报文后,回到 LISTEN 监听状态;

七、 在一个正常通信的 TCP 连接中,某一端崩溃了,另一端继续发送数据,会发生什么?

崩溃方的操作系统内核会发送一个 RST 重置报文到发送方,发送方收到 RST 报文后会终止连接。

八、 第三次握手失败了,会发生什么?

我们先看下基本的三次握手过程:

     TCP A                                                TCP B

  1.  CLOSED                                               LISTEN

  2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               --> SYN-RECEIVED

  3.  ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED

  4.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK>       --> ESTABLISHED

  5.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED

          Basic 3-Way Handshake for Connection Synchronization

在上述过程中的第 4 行,A 发送的应答报文丢失了,此时 B 处于 SYN-RECEIVED 状态,会发生什么?

  1. 当计时器超时了而 B 还没收到 A 的应答报文,B 就认为它之前发送的 SYN+ACK 报文已丢失,会触发超时重传,重传次数默认 5 次;
  2. 如果重传指定次数后还没收到应答,则 B 就会关闭连接;
  3. 此时 B 可能处于 SYN-RECEIVED 状态或者 CLOSED 状态:

    • CLOSED 状态:B 发送重置报文给 A,A 收到后就关闭连接;
    • SYN-RECEIVED 状态:如果 B 之后收到正常的应答报文(第 4 行),那么连接成功建立;如果收到传输数据的请求(第 5 行),那么连接也能成功建立,因为传输数据的报文包含了应答号;

参考资料

  1. RFC-793
  2. 小林coding
  3. 全网最具深度的三次握手、四次挥手讲解

把酒言欢
2 声望1 粉丝

苟日新,日日新。