根据以往面经,计网部分很少有相关知识的深刻问题,主要问题集中在TCP的三次握手和四次挥手方面。 根据自己的理解来进行以下记忆。

发布时间 2023-10-23 18:02:54作者: 沐霖悠月

SYN(Synchronize)和ACK(Acknowledge)是TCP协议中用于连接建立和数据传输的两个非常重要的标志位。

建立一个 TCP 连接需要“三次握手”,缺一不可:

  • 一次握手:client客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入 SYN_SEND 状态,等待服务器的确认;

  • 发起方:客户端

  • 描述:客户端选择一个初始序列号x,然后发送一个SYN报文段到服务器,该报文段中的序列号字段的值就是这个x。此时,客户端进入SYN_SENT状态。

  • 数据包标志:SYN=1, ACK=0

 

  • 二次握手:server服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 –> 客户端,然后服务端进入 SYN_RECV 状态

  • 发起方:服务器

  • 描述:服务器接收到客户端的SYN报文段后,需要回应确认。为此,它选择一个初始序列号y,并使用x+1作为确认号(表示服务器期望接收的下一个字节的序列号)。此时,服务器进入SYN_RECEIVED状态。

  • 数据包标志:SYN=1, ACK=1

 

  • 三次握手:client客户端发送带有 ACK(ACK=y+1) 标志的数据包 –> 服务端,然后客户端和服务器端都进入ESTABLISHED 状态,完成 TCP 三次握手。

  • 发起方:客户端

  • 描述:客户端收到服务器的SYN-ACK报文段后,会再次发送一个ACK报文段给服务器。该报文段的确认号应为y+1,表示客户端期望接收的下一个字节的序列号。此时,客户端进入ESTABLISHED状态。完成上述三次握手后,服务器也进入ESTABLISHED状态,这时TCP连接建立成功,双方都可以开始发送数据。

  • 数据包标志:SYN=0, ACK=1

 

如何理解客户端发送:SYN,服务器响应:SYN-ACK,客户端再次响应:ACK?

理解TCP的三次握手中的SYNSYN-ACKACK步骤的关键在于明确这个过程的目的:确保两台计算机(通常被称为客户端和服务器)都准备好双向通信

下面是对这三个步骤的更深入解释:

  1. 客户端发送:SYN

    • 当客户端希望与服务器建立一个新的TCP连接时,它会发送一个SYN(Synchronize)报文段。

    • 这个SYN报文段包含一个客户端选择的初始序列号(假设为x)。

    • 通过这个SYN报文段,客户端实际上在告诉服务器:“我想与你建立连接,并从序列号x开始传输数据”。

  2. 服务器响应:SYN-ACK

    • 服务器收到客户端的SYN报文段后,为了确认并响应这个连接请求,服务器会发送一个SYN-ACK(Synchronize-Acknowledge)报文段。

    • 这个SYN-ACK报文段包含两个关键的信息:

      1. ACK:它确认了服务器已经收到了客户端的SYN报文段。这个确认号将是x+1,表示服务器已经准备好接收序列号为x+1的客户端数据。

      2. SYN:服务器同样选择一个初始序列号(假设为y)来开始它的数据传输。通过这个SYN,服务器告诉客户端:“我也准备好了,并从序列号y开始传输数据”。

  3. 客户端再次响应:ACK

    • 客户端收到服务器的SYN-ACK报文段后,需要向服务器确认它已经收到了这个响应。

    • 客户端发送一个ACK(Acknowledge)报文段,确认号是y+1,表示客户端已经准备好接收序列号为y+1的服务器数据。

简化来说,这个三次握手过程实现了以下目标:

  • 确保客户端能够发送数据到服务器(通过SYN报文段)。

  • 确保服务器能够接收客户端的数据并发送数据到客户端(通过SYN-ACK报文段)。

  • 确保客户端能够接收服务器的数据(通过ACK报文段)。

完成这三个步骤后,TCP连接被建立,并且双方都可以开始双向通信。

 

为什么要用三次?而不是四次和两次?

因为既要保证数据可靠传输,又要提高传输的效率,而用三次恰恰可以满足以上两方面的需求!

二次握手的过程

  • 假设使用二次握手:客户端发送SYN,服务器回复SYN-ACK,然后连接建立。

  • 问题是:如果客户端的SYN报文段在网络中延迟了,并在很长时间后突然到达了服务器,那么服务器可能会误认为这是一个新的连接请求,并建立一个不需要的连接。而客户端可能早已放弃或建立了新的连接。这会导致服务器浪费资源。

  • 结论:二次握手不足以解决“旧报文段”的问题。

 

断开一个 TCP 连接则需要“四次挥手”,缺一不可:

  1. 第一次挥手:客户端发送一个 FIN(SEQ=x) 标志的数据包->服务端,用来关闭客户端到服务器的数据传送。然后客户端进入 FIN-WAIT-1 状态。

    FIN从主动关闭方到被动关闭方:

    • 当一个端(例如客户端)完成了数据发送并决定关闭连接时,它会发送一个FIN(Finish)标志的报文段到另一端(例如服务器)。这意味着:“我已经完成了数据发送,不再发送更多的数据”。

    • 客户端进入FIN_WAIT_1状态。

 

  1. 第二次挥手:服务器收到这个 FIN(SEQ=X) 标志的数据包,它发送一个 ACK (ACK=x+1)标志的数据包->客户端 。然后服务端进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2 状态。

    ACK从被动关闭方到主动关闭方:

    • 服务器收到这个FIN报文段后,会发送一个ACK(Acknowledge)来确认已收到此FIN报文段。这意味着:“我知道你想关闭连接”。

    • 客户端接收到ACK后,进入FIN_WAIT_2状态,等待服务器发送它的FIN报文段。

    • 服务器此时还可以继续发送数据。

 

  1. 第三次挥手:服务端发送一个 FIN (SEQ=y)标志的数据包->客户端,请求关闭连接,然后服务端进入 LAST-ACK 状态。

    FIN从被动关闭方到主动关闭方:

    • 当服务器发送完所有数据后,它也会发送一个带有FIN标志的报文段,表示:“我已经完成了数据发送,也不再发送更多的数据”。

    • 服务器进入LAST_ACK状态。

       

  1. 第四次挥手:客户端发送 ACK (ACK=y+1)标志的数据包->服务端,然后客户端进入TIME-WAIT状态,服务端在收到 ACK (ACK=y+1)标志的数据包后进入 CLOSE 状态。此时如果客户端等待 2MSL 后依然没有收到回复,就证明服务端已正常关闭,随后客户端也可以关闭连接了。

    ACK从主动关闭方到被动关闭方:

    • 客户端收到服务器的FIN报文段后,会发送一个ACK报文段来确认。

    • 客户端进入TIME_WAIT状态,持续一段时间(通常是2倍的MSL,最大报文寿命),以确保ACK报文段能够被服务器接收。这段时间也是为了处理可能在网络中延迟的数据包。

    • 服务器收到这个ACK报文段后,关闭连接,进入CLOSED状态。

    • 当客户端的TIME_WAIT计时器到期后,它也进入CLOSED状态。

    注意:虽然经常说客户端和服务器,但TCP四次挥手在任何一端都可以开始。主动发起关闭连接的端被称为“主动关闭方”,而另一端为“被动关闭方”。

    这个四次挥手过程确保了双方都能确认连接的终止,并确保双方都已完成数据传输。

只要四次挥手没有结束,客户端和服务端就可以继续传输数据!

 

为什么要四次挥手?

TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。

举个例子:A 和 B 打电话,通话即将结束后。

  1. 第一次挥手:A 说“我没啥要说的了”

  2. 第二次挥手:B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话

  3. 第三次挥手:于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”

  4. 第四次挥手:A 回答“知道了”,这样通话才算结束。

为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?

因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送。

如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?

客户端没有收到 ACK 确认,会重新发送 FIN 请求。