TCP四次挥手及状态解析

天气真好

Posted by bbkgl on March 15, 2020

试问岭南应不好

却道:此心安处是吾乡

理解TCP本身是基于连接的全双工协议,就能够理解TCP的三次握手和四次挥手了,三次握手的目的是确认双方都能收发数据了,随后就能进行通信了;而四次挥手的目的就是双方数据都发送完了,都确认要断开了。

TCP四次挥手

建立连接需要三次握手,断开连接需要四次握手,可以形象的比喻为下面的对话:

  • 套接字A:“任务处理完毕,我希望断开连接。”
  • 套接字B:“哦,是吗?请稍等,我准备一下。”
  • 等待片刻后……
  • 套接字B:“我准备好了,可以断开连接了。”
  • 套接字A:“好的,谢谢合作。”

给个图更加清晰:

20200314200538.png

与三次握手一样,四次握手的关键仍然是把各个序号理清楚。以图为例,简述一下过程:

  1. 客户端调用close()函数,发出了第一个TCP数据包,其中标志位FIN置为1,序号值为5000,此时客户端进入状态FIN_WAIT_1;
  2. 服务端收到后,发现FIN标志位为1,知道客户端是要关闭连接了,于是马上回复确认包,ACK位置1,进入CLOSE_WAIT状态,而客户端收到确认后,就进入了FIN_WAIT_2;
  3. 服务端在确认所有数据发送完后,也会发送FIN包,此时也会把ACK标志位置为1,并加上序列号,进入LAST_ACK状态,等待客户端回复确认;
  4. 客户端收到服务端的FIN包,立即回复确认,确认号等于接受到的序列号+1,于是进入了TIME_WAIT状态。

半关闭状态是什么?

一般在发送完最后一个包后,都是半关闭状态,即关闭了输入端,此时还是能接收对方的数据包的,只是发送通道已经关闭了。有个shutdown就是干这个事情的!

为什么是四次挥手

作为全双工的基于连接的协议,前面三次握手的时候已经讲了,要确认可靠的通信,三次是理论值的下限,同样的确认可靠的断开,最少也要三次。。。

那为什么是四次挥手呢?

看下多出的那次就是服务端把回复客户端的确认ACK包和自己的FIN包分开了。那为为什么不放一起呢?因为这时候,服务端并不一定完成所有数据的发送了,所以立即给对方回复了ACK包,表示我知道你要关闭连接了,随后自己处理完数据以后再发FIN包。

TCP挥手状态解析

基本上主动方和被动方的每次收发,就会有状态的改变。

主动方依次:

  • ESTABLISHED:正常建立连接,收发数据中
  • FIN_WAIT_1:发送完了FIN包,等待对方回复确认的ACK包,因为ACK包都是立即发送的,所以实际很短
  • FIN_WAIT_2:收到了对方回复确认的ACK包,继续等待对方的FIN包。二者区别在于这个FIN_WAIT_2要久一些,而1则较短,一般抓不到
  • TIME_WAIT,指收到对方的FIN包后,回复完ACK包后就进入了等待状态!这个经常考,助理假设主动方(通常是客户端)为A,被动方为B(通常是服务端):
    • 这样等的目的是什么?
      • A回复的ACK包对方可能收不到,这样服务端B就会超时重发FIN包,A等的这个时间就是收到这个B重发的FIN包然后回复确认。否则的话B重发的FIN包,就会被A回复成RST包,这样会造成被动方B的异常。
      • 如果没有这个TIME_WAIT,还可能导致新连接的异常。比如A发完ACK后马上进入可连接状态,这时候A和B立即建立连接,大部分情况下是正常的,可是如果A的某个本被超时丢弃的包又到了B,这时候B就认为这个包是A刚刚发过来的,造成连接的混乱,所以需要让A在TIME_WAIT的时间里无法建立新连接。
    • TIME_WAIT有多久,为什么是这么久(2MSL)?MSL的意思是最大分段寿命,也就是一个包在网络里待这么久,就会被丢弃,不再被转发。上述的第二种情况正好是两次网络交互,首先A的超时包经历了重重坎坷以MSL的时间到了B,B因为已经断开连接了,就相应RST包,这时候的RST包就会在A的TIME_WAIT里被A收到,而不会影响到A的新连接了,此时A就没有新连接。而超出2MSL时间的任何包,都被网络丢弃了,不会影响到新连接!简单地说,TIME_WAIT就是故意不让A利用重复的端口和IP建立新连接,因为这个新连接可能是个不安全的连接!这一点大家可以自己试试,主动方关闭后,会存在一段时间不让在重复的端口上建立新连接
    • 所以说,TIME_WAIT就是因为这么麻烦,才经常被考,实际上可以通过端口复用避免。
  • CLOSED:这个没什么秘密。。

被动方依次:

  • ESTABLISHED:正常建立连接,收发数据中
  • CLOSE_WAIT:收到主动放的FIN包,立即回复ACK,等着自己数据发送完成发送FIN包
  • LAST_ACK:等所有数据发送完成,发送完自己的FIN包,等待对方响应ACK
  • CLOSED:收到对方的ACK包后彻底关闭连接!

状态主要就是TIME_WAIT名堂多!