zoukankan      html  css  js  c++  java
  • TCP断开连接的过程

    黄色框线里面表示客户端请求关闭连接。

    补充细节(来自网络):

    关于以上的四次握手,我补充下细节:
    1. 默认情况下(不改变socket选项),当你调用close( or closesocket,以下说close不再重复)时,如果
    发送缓冲中还有数据,TCP会继续把数据发送完。
    2. 发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送),但是还可以接收数据。
    3. 应用层如何知道对端关闭?通常,在最简单的阻塞模型中,当你调用recv时,如果返回0,则表示对端
    关闭。在这个时候通常的做法就是也调用close,那么TCP层就发送FIN,继续完成四次握手。如果你不调用
    close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。这个可以写代码试试。
    4. 在很多时候,TCP连接的断开都会由TCP层自动进行,例如你CTRL+C终止你的程序,TCP连接依然会正常关
    闭,你可以写代码试试。

    ---------------------------------下面来自网络-------------------------------------------------------------

    TCP状态转移要点
        TCP协议规定,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不会被释放。网络服务器程序要同时管理大量连接,所以很有必要保证无用连接完全断开,否则大量僵死的连接会浪费许多服务器资源。在众多TCP状态中,最值得注意的状态有两个:CLOSE_WAIT和TIME_WAIT。
    1、LISTENING状态
      FTP服务启动后首先处于侦听(LISTENING)状态。
    2、ESTABLISHED状态
      ESTABLISHED的意思是建立连接。表示两台机器正在通信。
    3、CLOSE_WAIT
        对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭
    4、TIME_WAIT
        我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT。TCP协议规定TIME_WAIT状态会一直持续2MSL(即两倍的分段最大生存期),以此来确保旧的连接状态不会对新连接产生影响。处于TIME_WAIT状态的连接占用的资源不会被内核释放,所以作为服务器,在可能的情况下,尽量不要主动断开连接,以减少TIME_WAIT状态造成的资源浪费。
        目前有一种避免TIME_WAIT资源浪费的方法,就是关闭socket的LINGER选项。但这种做法是TCP协议不推荐使用的,在某些情况下这个操作可能会带来错误。
     
    在连接撤销过程中,有如下四个过程:   


    1.         HOST1上的应用程序关闭己方的连接导致TCP发送一个FIN消息给HOST2。
    2.         HOST2发送一个确认消息给HOST1,并且HOST2把FIN作为EOF递交给HOST2上的应用程序。
    3.         一段时间过后,HOST2上的应用程序关闭它那边的连接,引发一个FIN消息给HOST1。
    4.         HOST1给HOST2发送一个确认消息,然后HOST2关闭连接并释放资源,然而,HOST1却没有关闭连接,而是进入了TIME_WAIT状态,并为两个最大段生存时间(2MSL)保留在此状态.


    为什么需要time_wait?
    1.       因为在第四步的时候,HOST1发送的ACK可能丢失并导致HOST2重新发送FIN消息,TIME_WAIT维护连接状态.
           如果执行主动关闭的一方HOST1 不进入到TIME_WAIT状态就关闭连接那会发生什么呢?当重传的FIN消息到达时,因为TCP已经不再有连接的信息了,所以就用RST(重新启动)消息应答,导致HOST2进入错误的状态而不是有序终止状态,如果发送最后ACK消息的一方处于TIME_WAIT状态并仍然记录着连接的信息,它就可以正确的响应对等方HOST2的FIN消息了.
    2.       TIME_WAIT为连接中”离群的段”提供从网络中消失的时间.
             考虑一下,如果延迟或者重传段在连接关闭后到达时会发生什么呢?通常情况下,因为TCP仅仅丢弃该数据并响应RST消息,所以这不会造成任何问题。当RST消息到达发出延时段的主机时,因为该主机也没有记录连接的任何信息,所以它也丢弃该段。然而,如果两个相同主机之间又建立了一个具有相同端口号的新连接,那么离群的段就可能被看成是新连接的,如果离群的段中数据的任何序列号恰恰在新连接的当前接收窗口中,数据就会被重新接收,其结果就是破坏新连接。
     
    一、TCP连接关闭的几种方式:
    1、“正常”关闭:调用close()关闭socket、没close但进程正常结束(当然这是不应该的做法)、进程core掉、在shell命令行中kill掉进程,都可抽象成“正常”关闭。因为即使core掉,内核也会马上帮应用程序回收(close)socket文件描述符。
         “正常”关闭,默认情况下(非默认即设置Linger下面会介绍),关闭端即客户端TCP层会发FIN包,对端即服务器TCP层收到后,回ACK,客户端进入FIN_WAIT2状态。此时,TCP终止连接的4个分组中服务器应该发的第3个分组FIN包,其TCP层是不会主动发的,只有服务器端socket“正常”关闭,才会发出这个FIN包。至此,客户端进入TIME_WAIT状态。
    2、“非”正常关闭:客户端崩溃了,此时肯定发不出FIN包了(当然啦,内核都没机会帮应用程序回收资源了)。这种情况,服务器端有如下两种情况:
        A、服务器send数据,因为客户端已经崩溃,服务器收不到ACK自然会不停的重传。源自
            Berkeley的重传机制,重传8次,相对第一次传的15分钟后仍没收到ACK,则返回
            ETIMEDOUT或EHOSTUNREAC错误。如果服务器不理会这个错误,再次调用send,则
            立马返回Broken Pipe错误。    
           注:15分钟超时可以在 /proc/sys/net/ipv4/tcp_retries2 中修改
       B、 服务器不发任何数据了,那只有靠应用层心跳检测机制或Keepalive,来发觉TCP断连了。
    二、SO_LINGER套接口选项
               A、l_onoff设置为0,这也是默认情况,函数close()是立即返回的,然后TCP连接双方是通过
                    FIN、ACK4分组来终止TCP连接的。当然,发送缓冲区还有数据的话,系统将试着将这些数据
                    发送到对方。
               B、l_onoff非0,l_linger设置0,函数close()立即返回,并发送RST终止连接,发送缓冲区的数据丢弃。
               C、l_onoff非0,l_linger非0,函数close()不立即返回,而是在(a)发送缓冲区数据发送完并得到确认
                     (b)l_linger延迟时间到,l_linger时间单位为微妙。两者之一成立时返回。如果在发送缓冲区数据发送
                    完并被确认前延迟时间到的话,close返回EWOULDBLOCK(或EAGAIN)错误。
    三、客户端TCP连接“正常”关闭,服务器的几种情况:
              情形 客户端l_onoff设置为0, 
    “正常”关闭 客户端l_onoff非0,l_linger设置0,“正常”关闭 
    服务器阻塞模式send,正阻塞在send函数未返回 客户端TCP发送FIN,服务器send函数返回成功(返回字节数是实际拷贝到发送缓冲区的字节数)。客户端发送RST。如果服务器再次调用send,将返回errno[32]:Broken pipe 客户端TCP发送RST,服务器函数返回成功(返回字节数是实际拷贝到发送缓冲区的字节数)。若服务器再次调用send,则返回-1,errno[104]:Connection reset by peer。若再次调用send,则返回-1,errno[32]:Broken pipe 
    服务器空闲 客户端TCP发送FIN,若服务器没理会而调用send,客户端发送RST,send返回-1,errno[32]:Broken pipe 客户端TCP发送RST,若服务器没理会而调用send,send返回-1,errno[104]:Connection reset by peer。若再次调用send,则返回-1,errno[32]:Broken pipe
    总之,1、收到对端RST后,仍然调入send(),则返回Connection reset by peer,再次调用send(),则返回Broken pipe
             2、收到对端FIN后,仍然调研哪个send(),直接返回Broken pipe
  • 相关阅读:
    H5图片上传、压缩
    数据库基本操作
    数组遍历
    CURL
    获取IP
    Memcached的实战笔记
    修bug总结 (基于java语言)
    java开发工作的总结
    多线程测试类
    可清除的单例对象获取类
  • 原文地址:https://www.cnblogs.com/gmth/p/3180357.html
Copyright © 2011-2022 走看看