TCP主动关闭连接原理简介

        在编写网络通信模块时,经常会遇到主动断开连接和被动断开连接的情况,针对主动断开连接时,要执行4次握手过程。而非正常断开连接有多种,比如说 连接的一端执行kill -9时,另一端需要针对 -1 状态码做处理,然后调用close主动断开连接。还有一种情况是链接的一端宕机(拔网线、掉电),这种情况请参考 “TCP长连接一端宕机(掉电,拔网线)会发生什么?”

        这里主要说的是主动关闭连接情况。关闭4次握手过程如下图:

TCP关闭4次握手过程.jpg

        假如连接的两端分别为客户端(C)和服务端(S)。

        一、C/S 执行close 客户端(C)或者服务端(S)一端执行关闭时,另一端会捕获到 -1 状态码,当获取到 -1 状态码时应该主动关闭,否则当前连接为坏连接。

看到这点的时候很多人认为很简单,但在工作中发现有业务线没有针对-1做处理,很容易引发别的问题。

        二、执行close 底层发生了什么

        按照上图中主动关闭状态图看,如果一端主动执行close时应该发送 fin 包,但真的是这样吗?

在做网络框架开发时发现一条tcp链接在close时,通过抓包看close在系统调用的时候,发送端发出的是rst报文, 而不是正常的fin而另一端会收到econnrest,而不是正常的fin包。如下:

17:26:09.521258 IP localhost.13600 > x.x.58.86.http: R 19:19(0) ack 523 win 9509

        可以清楚的看到 R 19:19(0) ack 523 win 9509,发的是rst包,为什么呢?

        看看代码实现了什么:

net/ipv4/tcp.c

/* As outlined in RFC 2525, section 2.17, we send a RST here because
      * data was lost. To witness the awful effects of the old behavior of
      * always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
      * GET in an FTP client, suspend the process, wait for the client to
      * advertise a zero window, then kill -9 the FTP client, wheee...
      * Note: timeout is always zero in such a case.
      */
     if (data_was_unread) {
          /* Unread data was tossed, zap the connection. */
          NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
          tcp_set_state(sk, TCP_CLOSE);
          tcp_send_active_reset(sk, sk->sk_allocation);
     }

        从代码可以看出,当接收缓冲区还有数据,协议栈就会发rst代替fin,所以上面为什么会发rst而不是fin了。

        在我们程序中可以在调用close时把缓存区的数据手动拉取处理完,这样就可以发送fin包了。


原创文章,转载请注明:转载自Onstepr博客 | Onstepr Blog – Onstepr.com

评论 (0)
  1. 暂无评论

当前禁止评论