TCP的3次握手和4次挥手协议及数据传输过程

  1. TCP建立连接3次握手

    握手过程如下:

    1. 客户端A向服务器发送TCP连接请求数据包,报文设置同步标志位SYN=1,客户端序号seq=x(表明传输数据时的第一个数据字节的序号是x),A然后进入SYN-SENT状态;

    2. 服务器B收到连接请求后,发回连接确认数据包,报文设置同步标志位SYN=1,服务端序号seq=y,以及对客户端的确认号ack=seq(A)+1=x+1,B然后接入SYN-RCVD状态;

    3. 客户端A收到服务器B的确认数据包后,进一步做确认,发送一个SYN=0,seq=x+1,ack=seq(B)+1=y+1的确认报文,A然后进入数据传输状态ESTABLISHED,服务器B收到客户端A的确认报文后也进入数据传输状态ESTABLISHED;


      连接流程

    那为什么只是3次握手呢?
    举个随便找人聊天的例子:

      A对B说:喂,你听得到我说话吗?
      B对A回:嗯,我听到的你说话,你听得到我说话吗?
      A回B说:可以听到,我们开始聊天把!
      BABALA的聊天
    

    应该把网络状态想象为不可靠的,3次握手可以保证任何一次握手出现问题都可以发现或补救:

    1. 第一次握手A发送SYN传输失败,A,B都不会申请资源,连接失败。如果一段时间内发出多个SYN连接请求,那么B只会接受它最后发送的那个SYN的SYN+ACK回应,忽略其他回应全部回应,B中多申请的资源也会释放;

    2. 第二次握手B发送SYN+ACK传输失败,A不会申请资源,B申请了资源,但收不到A的ACK,过一段时间释放资源。如果是收到了多个A的SYN请求,B都会回复SYN+ACK,但A只会承认其中它最早发送的那个SYN的回应,并回复最后一次握手的ACK;

    3. 第三次握手ACK传输失败,B没有收到ACK,释放资源,对于后序的A的传输数据返回RST。实际上B会因为没有收到A的ACK会多次发送SYN+ACK,次数是可以设置的,如果最后还是没有收到A的ACK,则释放资源,对A的数据传输返回RST。

  2. 数据传输

  3. TCP释放连接4次握手

    四次握手过程如下:

    1. 客户端A主动关闭连接,A发送一个终止标志位FIN=1,客服端序号seq=u的数据包到服务器B,然后A进入FIN-WAIT-1状态,表示A没有数据要传输给服务器B,此时A处于半连接状态;

    2. 服务器B收到FIN数据包,就返回一个ACK,同意关闭连接,seq=v,确认号ack=u+1,此时服务端B进入CLOSE-WAIT状态;

    3. 客户端A收到这个数据包后,进入FIN-WAIT-2状态,此时仍然可以接收服务器B发送过来的数据包;

    4. 服务器B没有数据数据要传输给客户端A,也发送一个一个终止标志位FIN=1,seq=w,ack=u+1的确认数据包,尔后进入LAST-ACK状态。客户端A收到服务器B的FIN关闭确认数据包后,给服务器B发回一个确认包,seq=u+1,ack=w+1,进入TIME-WAIT状态,等待2MSL时间后进入CLOSED状态,服务器B接收到客户端的ACK确认报文后进入CLOSED关闭状态,此时服务器B若没有收到这个ACK,服务器B的超时重传机制会重新发送一个关闭确认数据包。


      释放流程

    问答:

    1. 那为什么是4次握手呢?
      答:因为TCP是全双工通信,每个方向上都要进行单独关闭,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

    2. 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间 Maximum Segment Lifetime)才能返回到CLOSE状态?
      答:

      1. 为了保证主动关闭方的最后一个ACK报文能够到达被动关闭方,假设这个ACK报文丢失了,处于LAST-ACK状态的被动关闭方在规定的时间内没有收到这个ACK报文,或超时重传FIN报文,而主动关闭方在等待2MSL时间内收到这个重传的报文会重新回复一次确认,重新启动2MSL计时器。如果主动关闭方在TIME-WAIT状态内不等待2MSL的话,就无法收到重传报文。
      2. 主动关闭方在等待2MSL时间后,可以使得本连接持续时间每产生的所有报文都过期了,使得下一次新的连接不会出现旧的连接报文。
    3. 为什么会出现大量的CLOSE-WAIT状态的连接,如何解决?
      答:

      1. 产生原因
        • CLOSE_WAIT产生的原因是主动关闭方主动关闭,被动关闭方收到FIN包,应用层却没有做出关闭操作引起的。也就是说,在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。
        • 出现大量CLOSE_WAIT的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。
      2. 解决方案
        • 通过优化系统内核参数可容易解决
          vi /etc/sysctl.conf 
          #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
          net.ipv4.tcp_keepalive_time=1200   
          #生效,如下命令 
          /sbin/sysctl -p
          
        • 需要从程序本身出发
        • 可以使用TCP的keepalive功能,让操作系统替我们自动清理掉CLOSE_WAIT连接
    4. 为什么会出现大量的TIME-WAIT状态的连接,如何解决?
      答:

      • 产生原因
        在通讯过程中A主动关闭造成的, 在A发送了最后一个FIN包后,系统会等待 Double时间 的MSL(Max Segment Lifetime)【注:按不同的操作系统有不同时间】用于等待接受B发送过来的FIN_ACK和FIN, 这段时间A的对应的socket的fd是不能够重新利用的, 这样在大量的短连接服务中,会出现TIME_WAIT过多的现象。
      • 解决方法
        1. 通过优化系统内核参数,调整TIME_WAIT超时时间

          vi /etc/sysctl.conf 
          #表示开启SYN Cookies。 
          #当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭 
          net.ipv4.tcp_syncookies = 1 
          #表示开启重用。 
          #允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭 
          net.ipv4.tcp_tw_reuse = 1 
          #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭 
          net.ipv4.tcp_tw_recycle = 1 
          #表示如果套接字由本端要求关闭。 
          #这个参数决定了它保持在FIN-WAIT-2状态的时间 
          #生效,如下命令 
          /sbin/sysctl -p
          

你可能感兴趣的:(TCP的3次握手和4次挥手协议及数据传输过程)