TCP:四次挥手

四次挥手

前言

所谓的四次挥手即 TCP 连接的释放(解除)。数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于 ESTABLISHED 状态,然后客户端主动关闭,服务器被动关闭。

 

四次挥手

第一次:客户端进程发出连接释放报文,并且停止发送数据

  • 标记位为 FIN=1,表示“请求释放连接“;
  • 序号为 seq=U;客户端的序列号
  • 随后客户端进入 FIN-WAIT-1(终止等待1)阶段,即半关闭阶段。并且停止在客户端到服务器端方向上发送数据,但是客户端仍然能接收从服务器端传输过来的数据。
  • 注意:这里不发送的是正常连接时传输的数据(非确认报文),而不是一切数据,所以客户端仍然能发送 ACK 确认报文。
  • TCP 规定,FIN 报文段即使不携带数据,也要消耗一个序号。

第二次:服务器收到连接释放报文,发出确认报文

  • 此时,服务器端结束 ESTABLISHED 阶段,进入 CLOSE-WAIT 阶段(半关闭状态)
  • 标记位为 ACK=1,表示“接收到客户端发送的释放连接的请求”;
  • 序号为 seq=V;服务端的序列号
  • 确认号为 ack=U+1,表示是在收到客户端报文的基础上,将其序号 seq 值加 1 作为本段报文确认号 ack 的值;
  • 随后服务器端开始准备释放服务器端到客户端方向上的连接。
  • 客户端收到从服务器端发出的 TCP 报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束 FIN-WAIT-1 阶段,进入 FIN-WAIT-2 阶段

第三次:服务器端自从发出 ACK 确认报文之后,经过 CLOSED-WAIT 阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段 TCP 报文

  • 标记位为 FIN=1,ACK=1,表示“已经准备好释放连接了”。注意:这里的 ACK 并不是确认收到服务器端报文的确认报文。
  • 序号为 seq=W;
  • 确认号为 ack=U+1;表示是在收到客户端报文的基础上,将其序号 seq 值加1作为本段报文确认号 ack 的值。
  • 随后服务器端结束 CLOSE-WAIT 阶段,进入 LAST-ACK 阶段。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。

第四次:客户端收到从服务器端发出的 TCP 报文,确认了服务器端已做好释放连接的准备,结束 FIN-WAIT-2 阶段,进入 TIME-WAIT 阶段,并向服务器端发送一段报文

  • 标记位为 ACK=1,表示“接收到服务器准备好释放连接的信号”。
  • 序号为 seq=U+1;表示是在收到了服务器端报文的基础上,将其确认号 ack 值作为本段报文序号的值。
  • 确认号为 ack=W+1;表示是在收到了服务器端报文的基础上,将其序号 seq 值作为本段报文确认号的值。
  • 随后客户端开始在 TIME-WAIT 阶段等待 2MSL
  • 注意此时TCP连接还没有释放,客户端必须经过 2MSL(最长报文段寿命)的时间后,当客户端撤销相应的 TCB 后,才进入 CLOSED 状态。

后续

  • 服务器端收到从客户端发出的 TCP 报文之后结束 LAST-ACK 阶段,撤销 TCB,进入 CLOSED 阶段。由此正式确认关闭服务器端到客户端方向上的连接。可以看到,服务器结束 TCP 连接的时间要比客户端早一些
  • 客户端等待完 2MSL 之后,结束 TIME-WAIT 阶段,进入 CLOSED 阶段,由此完成“四次挥手”。
  • 后“两次挥手”既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成“四次挥手”。
  • 与“三次挥手”一样,在客户端与服务器端传输的 TCP 报文中,双方的确认号 ack 和序号 seq 的值,都是在彼此 ack 和 seq 值的基础上进行计算的,这样做保证了 TCP 报文传输的连贯性,一旦出现某一方发出的 TCP 报文丢失,便无法继续"挥手",以此确保了"四次挥手"的顺利完成。

为什么客户端在 TIME-WAIT 阶段要等 2MSL?

为的是确认服务器端是否收到客户端发出的 ACK 确认报文

当客户端发出最后的 ACK 确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完 ACK 确认报文之后,会设置一个时长为 2MSL 的计时器。

MSL 指的是 Maximum Segment Lifetime:一段 TCP 报文在传输过程中的最大生命周期。2MSL 即是服务器端发出为 FIN 报文和客户端发出的 ACK 确认报文所能保持有效的最大时长。

服务器端在 1MSL 内没有收到客户端发出的 ACK 确认报文,就会再次向客户端发出 FIN 报文;

  • 如果客户端在 2MSL 内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的 ACK 确认报文。客户端再次向服务器端发出 ACK 确认报文,计时器重置,重新开始 2MSL 的计时;
  • 否则客户端在 2MSL 内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了 ACK 确认报文,客户端可以进入 CLOSED 阶段,完成“四次挥手”。

 

为什么“握手”是三次,“挥手”却要四次?

TCP 建立连接时之所以只需要"三次握手",是因为在第二次"握手"过程中,服务器端发送给客户端的 TCP 报文是以 SYN 与 ACK 作为标志位的。SYN 是请求连接标志,表示服务器端同意建立连接;ACK 是确认报文,表示告诉客户端,服务器端收到了它的请求报文。

即 SYN 建立连接报文与 ACK 确认接收报文是在同一次"握手"当中传输的,所以"三次握手"不多也不少,正好让双方明确彼此信息互通。

TCP 释放连接时之所以需要“四次挥手”,是因为 FIN 释放连接报文与 ACK 确认接收报文是分别由第二次和第三次"挥手"传输的。为何建立连接时一起传输,释放连接时却要分开传输?

  • 建立连接时,被动方服务器端结束 CLOSED 阶段进入“握手”阶段并不需要任何准备,可以直接返回 SYN 和 ACK 报文,开始建立连接。
  • 释放连接时,被动方服务器,突然收到主动方客户端释放连接的请求时并不能立即释放连接,因为还有必要的数据需要处理,所以服务器先返回 ACK 确认收到报文,经过 CLOSE-WAIT 阶段准备好释放连接之后,才能返回 FIN 释放连接报文。

 

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP 还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。

服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为 2 小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。

若一连发送 10 个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

参考:https://blog.csdn.net/qzcsu/article/details/72861891

参考:https://www.cnblogs.com/AhuntSun-blog/p/12037852.html

原文地址:https://www.cnblogs.com/dc2019/p/13833099.html