TCP连接和各状态浅析

TCP协议中三次握手连接和四次挥手断开和其中的状态变化

TCP(Transmission Control Protocol)协议在OSI模型中属于传输层的协议(第4层)也是最常用的互联网协议,与UDP相比它是一种可靠的传输协议,建立和断开连接中一共有11种可能发生的状态(states)。

Linux系统中可以用ss -nat命令查看所有TCP连接情况如果是旧的系统没有此命令可以使用netstat代替只是输出格式不一致,统计当前所有TCP状态可以加管道实现 ss -nat | awk 'NR>1 { d[$1]++ } END { for (i in d) print i, d[i] }'

建立连接(三次握手)

TCP是面向连接的所以传输双方在传输前要建立一条连接后才能通信,建立连接后TCP协议提供全双⼯(就是可以同时发送和接收不影响)的通信服务。建立连接的过程我们给它取了个生动的名字叫“三次握手”,因为从发起到建立连接一共有三个步骤也就是双方会发三个包,先看下面的图

3-way-handshake

  1. 首先客户端发送SYN(Synchronous)包到服务端告诉服务器开始建立连接,包里面会包含一个随机数(j)序列号(Sequence Number),此时客户端进入SYC-SENT状态。当然最初服务器是处于LISTEN状态监听某一个端口,本文图片以绿底黑框的都代表TCP状态。
  2. 服务端收到SYN发送应答SYN-ACK(Acknowledgement)携带确认号码(Acknowledge Number)为收到的序列号+1也就是j+1,加上一个自己的SYC序列号随机数(k),服务端把状态置为SYN-RECEIVED
  3. 客户端收到SYN-ACK包并发送确认号码k+1,此时客户端的状态为ESTABLISHED,服务器收到ACK后状态也变为ESTABLISHED

以下是wireshake抓包后TCP连接的三次握手,这里序列号码jk都为0

wireshake-3-handshake

    TCP A (port 65525)                                     TCP B (port 443)

                                                           LISTEN
       (Start)
  1.  SYN-SENT    --> <SEQ=0><CTL=SYN>                 --> SYN-RECEIVE

  2.  ESTABLISHED <-- <SEQ=0><ACK=1><CTL=SYN,ACK>      <-- SYN-RECEIVE

  3.  ESTABLISHED --> <SEQ=1><ACK=1><CTL=ACK>          --< ESTABLISHED

断开连接(四次挥手)

断开连接称为“挥手”一共有四步每一方向占两步,由于TCP是全双工的所以关闭连接需要双方都完成关闭(close)才算结束,其中会涉及状态也就比握手多。客户端和服务端都可以发起挥手动作,先发送FIN包的动作叫主动关闭(active close)然后另一方回应ACK包叫执行被动关闭(passive colse),双方各完成一次主动和被动关闭一共四次。

4-way-handshake

  1. 客户端数据传输完毕发起断开连接(主动关闭),发送FIN包携带一个序列号(x)到服务端并更新自己的状态为FIN-WAIT-1
  2. 服务端收到FIN包返回ACK包(被动关闭),应答ACK确认序号为x+1,服务端此时的状态为CLOSE-WAIT。另一边客户端收到ACK更新状态为FIN-WAIT-2
  3. 服务端准备执行断开连接(主动关闭),此时服务端的状态还是CLOSE-WAIT,发送FIN包携带序列号(y)到客户端更新服务端状态为LAST-ACK,客户端收到FIN包后状态变为TIME-WAIT
  4. 客户端收到FIN应答ACK(被动关闭)携带确认序号为y+1,服务端收到ACK立即关闭连接状态变为CLOSED
  5. 因为客户端最后不知道服务端有没有收到ACK包,所以默认等待两倍的MSL(Maximum Segment Lifetime),Linux上MSL一般为60s,等待120s使服务器如果没有收到ACK也能重传FIN包,最后把自己的状态置为CLOSED在此之前的MSL时间中一直是TIME-WAIT状态

看一个现实中的例子ssh连接退出

wireshake-4-handshake

    TCP A (port 49612)                                     TCP B (port 22)

  1.  ESTABLISHED                                          ESTABLISHED

  2.  (Close)
      FIN-WAIT-1  --> <SEQ=277><ACK=401><CTL=FIN,ACK>  --> CLOSE-WAIT

  3.  FIN-WAIT-2  <-- <SEQ=401><ACK=278><CTL=ACK>      <-- CLOSE-WAIT

  4.                                                       (Close)
      TIME-WAIT   <-- <SEQ=401><ACK=278><CTL=FIN,ACK>  <-- LAST-ACK

  5.  TIME-WAIT   --> <SEQ=278><ACK=402><CTL=ACK>      --< CLOSED

  6.  (2 MSL)
      CLOSED

TCP状态迁移图

TCP一共有11种状态,虚线箭头表示客户端状态转换轨迹,实线表示服务端,但这里的客户端和服务端不是绝对的可以互换。

TCP state transition diagram

CLOSING状态

仔细看你会发现有一个CLOSING状态有些陌生,因为以上介绍的三次握手和四次挥手都是正常且顺序连接或断开中TCP状态,现实应用中比较复杂上面还有一个状态没有涉及到就是CLOSING

从上面状态转换图中可看到CLOSING状态发生的情况是在首先发起挥手的一方(比如说客户端)正常如上图在FIN-WAIT-1后会进入FIN-WAIT-2,可进入FIN-WAIT-2是有条件的那就是收到第一次发FIN后服务端返回的ACK,可此时客户端没有收到ACK反而收到了服务端的FIN包,按上面讲的这就乱序了,这种特殊情况就是CLOSING状态,也就是说进入CLOSING有下面三个条件

  • 主动关闭发送FIN包发起后(此时是FIN-WAIT-1)
  • 没有收到另一方的ACK包等待中
  • 收到另一方发送过来的 FIN

状态表

现在对所有状态做个总结

TCP connection state Description
LISTEN SOCKET监听中等待远程连接
SYN-SENT 发送连接请求SYN包后等待SYN-ACK包,第一次握手
SYN-RECEIVED 连接收到SYN包到并返回SYN-ACK包等待远端ACK,第二次握手
ESTABLISHED 连接已经建立,双方可以传输数据了,第三次握手
FIN-WAIT-1 发起主动关闭发送FIN包后等待ACK包,第一次挥手,另一方一般很快返回很少见到
CLOSE-WAIT 被动关闭收到对方FIN包,返回发送ACK包,在没有发起主动关闭之前一直使这个状态,第二次挥手
FIN-WAIT-2 收到发起主动关闭返回的ACK包,等待对方发送FIN包,现在只能接收数据不能发送数据,也叫半关闭状态,持续时间短,第二次挥手
LAST-ACK 进行一次被动关闭后,发起主动关闭等待ACK,第三次挥手
TIME-WAIT 主动关闭执行后,收到FIN包(第三次挥手),并返回ACK(第四次挥手) 等待两倍的MSL之后进入CLOSED
CLOSING 见上面
CLOSED 连接关闭没有连接

Reference

  1. www.ibm.com
  2. community.apigee.com
  3. blog.confirm.ch
  4. blog.csdn.net