一个解除TCP连接的TIME_WAIT状态限制的简便方法

原创 2015年11月18日 11:12:29
/******************************************************************************************
*              版权声明
*   本文为本人原创,本人拥有此文的版权。鉴于本人持续受益于开源软件社区,
* 本人声明:任何个人及团体均可不受限制的转载和复制本文,无论是否用于盈利
* 之目的,但不得修改文章内容,并必须在转载及复制时同时保留本版权声明,否
*  则为侵权行为,本人保留追究相应主体法律责任之权利。
*               speng2005@gmail.com
*                  2008-7
******************************************************************************************/

注:这是早年写的文章,原地址已时过境迁,现搬到此处。


    近日无意间发现了一个小窍门:当TCP连接所对应socket的接收队列中仍有未读数据时,将此socket强行close后,将使此socket连接不会进入TIME_WAIT状态,用"netstat -anp"命令查看可发现此连接将消失的无影无踪!上述情形在linux2.6.18-5-686-bigmem内核及winxp平台上验证通过。其他平台上,以及当socket的发送队列中仍有剩余数据未发送时强行close后是否可重现此现象,尚未进行验证。
    那么说了这么多,这个窍门有什么用呢?对TCP的socket编程比较熟悉的人都知道,通常情况下,当一个socket已成功地在服务端和客户端建立连接后,无论是哪一方,先行调用close的那一方所对应的socket最终必会进入TIME_WAIT状态,并且会持续2*MSL,大约1-4分钟,然后由操作系统自动回收并将TCP连接设为CLOSED初始状态。这个过程按下图中的TCP连接状态转换过程进行:


在socket的TIME_WAIT状态结束之前,该socket所占用的本地端口号将一直无法释放。编写过高TCP并发并且采用短连接方式进行通讯的软件程序的人,都可能体会到,这样的通讯系统在高并发高负载下运行一段时间后,就常常会出现做为客户端的程序无法向服务端建立新的socket连接的情况。此时用"netstat-anp"命令查看系统将会发现机器上存在大量处于TIME_WAIT状态的socket连接,并且占用大量的本地端口号。最后,当该机器上的可用本地端口号被占完,而旧的大量处于TIME_WAIT状态的socket尚未被系统回收时,就会出现无法向服务端创建新的socket连接的情况。此时的通讯系统几乎停转,空有再好的性能也发挥不出来。
    如果能够在客户端程序主动关闭socket之前,让该socket的接收队列中仍保留一些数据(至少要有多余的一个字节的数据),然后调用close关闭,那么上述的无法向服务端创建新的socket连接的情况将不会出现。这是因为当socket的接收队列中仍有数据未被应用程序读走就被强行关闭时,操作系统(至少在笔者验证过的操作系统上的确如此)的TCP/IP协议栈驱动程序会在底层主动向服务端发送一个要求结束TCP连接的控制包,并将该TCP包头的flag控制字段中的RESET位置位,从而迅速结束了此TCP连接。这其实是操作系统对TCP连接断开的一种异常处理。而正常情况下(socket的接收队列中无未读数据),当应该程序调用close关闭连接时,底层驱动程序向服务端发送的要求结束TCP连接的控制包头的flag控制字段中是将FIN位置位,并且严格遵循上图所示的状态转换过程,最终到达TIME_WAIT状态并持续2*MSL的时间。
    知道了上述原理,具体如何利用呢?这有两种思路。第一种,从服务端入手,在服务端向客户端返回所有有效数据后,再在后面插入若干填充字节;而在客户端将有效数据读完之后,将填充字节留在对应socket的接收队列中,然后直接关闭连接。这个思路的好处是对客户端程序不需要做改动。第二种思路是从客户端入手,在客户端程序中控制从socket的接收队列中recv数据的节奏,将最后一个有效字节留在接收队列中不取出。但是怎么知道哪里是最后一个字节呢?最后一个字节的内容是什么?解决第一个问题的方法就是制定客户端与服务端通讯协议时应该考虑提供某种机制,让客户端解包时能够知道数据包何时结束,例如可以在通讯包的头部指示整个包的长度。解决第二个问题的方法是调用recv时使用MSG_PEEK标志,例如:
ssize_t recvBytes = recv(socket,buf,buflen,MSG_PEEK);
    当以MSG_PEEK标志接收数据时,数据将只从socket的接收队列中拷贝到指定的buf中,而相应的数据不会被移出接收队列。这样就能够既获得应用程序所需数据,又能将数据留在socket接收队列中,进而能够在关闭时使该TCP连接不进入TIME_WAIT状态。第二种思路的好处是不需要对服务端程序进行改动。笔者已将第二种思路实现并验证可行。

备注:在网上,解除TIME_WAIT状态限制有多种方法,其中有种方法是在socket建立后调用setsockopt函数如下:
int option = 1;
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
如此就可以解决问题了。但实际上,有的平台不支持setsockopt函数,既使是在能够支持的平台,笔者经实际验证发现并不能达到目的。另有一些其他方法也各有利弊。所以,如果其他方法无效,本文中的方法就值得一试了。


一个解除TCP连接的TIME_WAIT状态限制的简便方法

近日无意间发现了一个小窍门:当TCP连接所对应socket的接收队列中仍有未读数据时,将此socket强行close后,将使此socket连接不会进入TIME_WAIT状态,用"netstat -an...
  • yhcs1213
  • yhcs1213
  • 2015年09月08日 17:04
  • 237

一个解除TCP连接的TIME_WAIT状态限制的简便方法

/************************************ *              版权声明 *   本文为本人原创,本人拥有此文的版权。鉴于本人持续受益于开源软件社区, ...
  • hualucky
  • hualucky
  • 2014年09月12日 22:29
  • 402

一个解除TCP连接的TIME_WAIT状态限制的简便方法

/************************************ *              版权声明 *   本文为本人原创,本人拥有此文的版权。鉴于本人持续受益于开源软件社区, ...
  • wyhang0
  • wyhang0
  • 2014年10月26日 22:36
  • 1630

一个解除TCP连接的TIME_WAIT状态限制的简便方法

近日无意间发现了一个小窍门:当TCP连接所对应socket的接收队列中仍有未读数据时,将此socket强行close后,将使此socket连接不会进入TIME_WAIT状态,用"netstat -an...
  • wangpengqi
  • wangpengqi
  • 2013年07月29日 15:10
  • 690

TCP/IP详解--TIME_WAIT状态存在的原因

1. 实际问题         初步查看发现,无法对外新建TCP连接时,线上服务器存在大量处于TIME_WAIT状态的TCP连接(最多的一次为单机10w+,其中引起报警的那个模块产生的TIME_WAI...
  • yusiguyuan
  • yusiguyuan
  • 2014年09月01日 20:15
  • 3329

TCP/IP详解--TIME_WAIT状态详解

Socket中的TIME_WAIT状态 在高并发短连接的server端,当server处理完client的请求后立刻closesocket此时会出现time_wait状态然后如果client再并发...
  • yusiguyuan
  • yusiguyuan
  • 2014年03月18日 10:29
  • 5651

服务器tcp连接timewait过多优化及详细分析

【背景说明】 在7层负载均衡上,查询网络状态发现timewait太多,于是开始准备优化事宜 整体的拓扑结构,前面是lvs做dr模式的4层负载均衡,后端使用(nginx、or haproxy...
  • chengfangang
  • chengfangang
  • 2015年11月11日 10:03
  • 1645

TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—基础知识篇

TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—基础知识篇  最近遇到一个线上报警:服务器出现大量TIME_WAIT导致其无法与下游模块建立新HTTP连接,在...
  • wyhang0
  • wyhang0
  • 2014年10月26日 22:50
  • 780

TCP/IP详解--TCP连接中TIME_WAIT状态过多

TIMEWAIT状态本身和应用层的客户端或者服务器是没有关系的。仅仅是主动关闭的一方,在使用FIN|ACK|FIN|ACK四分组正常关闭TCP连接的时候会出现这个TIMEWAIT。服务器在处理客户端请...
  • yusiguyuan
  • yusiguyuan
  • 2014年03月18日 10:30
  • 48870

为什么TCP的TIME_WAIT状态要保持2MSL?

TIMEWAIT状态也称为2MSL等待状态。 每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。 它是任何报文段被丢弃前在网络内的最长时间。 ...
  • unix21
  • unix21
  • 2013年11月24日 13:14
  • 3983
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一个解除TCP连接的TIME_WAIT状态限制的简便方法
举报原因:
原因补充:

(最多只允许输入30个字)