近日无意间发现了一个小窍门:当TCP连接所对应socket的接收队列中仍有未读数据时,将此socket强行close后,将使此socket连接不会进入TIME_WAIT状态,用"netstat -anp"命令查看可发现此连接将消失的无影无踪!上述情形在linux 2.6.18-5-686-bigmem内核及winxp平台上验证通过。其他平台上,以及当socket的发送队列中仍有剩余数据未发送时强行close后是否可重现此现象,尚未进行验证。
那么说了这么多,这个窍门有什么用呢?对TCP的socket编程比较认识的人都知道,通常情况下,当一个socket已成功地在服务端和客户端建立连接后,无论是哪一方,先行调用close的那一方所对应的socket最终必会进入TIME_WAIT状态,并且会持续2*MSL,大约1-4分钟,然后由操作系统自动回收并将TCP连接设为CLOSED初始状态。
在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(socketbufbuflenMSG_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函数,既使是在能够支持的平台,笔者经实际验证发现并不能达到目的。另有一些其他方法也各有利弊。所以,如果其他方法无效,本文中的方法就值得一试了。
分享到:
相关推荐
TIME_WAIT是TCP连接的一个正常终止状态,但若数量过多则可能会影响到服务器性能。本文将详细介绍如何在Linux系统中优化TIME_WAIT状态的连接,并提供具体的配置示例。 #### TCP TIME_WAIT状态简介 TCP协议在连接...
【Linux网络编程笔记】TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法,这是一个关于网络编程和Linux系统配置的问题。在TCP/IP通信中,TIME_WAIT状态是TCP连接生命周期的一部分,用于确保...
TIME_WAIT状态是TCP连接生命周期的一部分,它确保数据完全传输并且防止旧的数据包被误解释为新的连接初始化。然而,过多的TIME_WAIT状态会占用大量的连接资源,从而影响新连接的建立。在描述中提到,尽管已经尝试过...
当一个 TCP 连接关闭时,服务器端会在 TIME_WAIT 状态下等待一段时间,以确保所有的数据包都已经被客户端收到。在这个状态下,服务器端会等待两个最大段生命周期(Maximum Segment Lifetime,MSL)的时间,以确保...
在 TCP 连接中,TIME_WAIT 状态是一种特殊的状态,它表示客户端已经关闭连接,但是服务器端还没有关闭连接。TIME_WAIT 状态的出现是因为客户端需要等待服务器端的确认包,以确保连接已经完全关闭。 在 TIME_WAIT ...
TIME_WAIT是TCP协议中的一个状态,当一个TCP连接正常关闭后,会进入TIME_WAIT状态,等待一段时间(通常是2MSL,即最大段生命周期的两倍)来确保网络中没有残留的数据包。在这个状态下,端口被占用,不能立即复用,这...
TIME_WAIT是一种TCP连接的状态,当一个TCP连接被主动关闭时,客户端会进入TIME_WAIT状态,目的是确保任何可能在网络中滞留的数据包能够被正确处理,避免这些数据包在未来的新连接中被误解释。然而,在高并发场景下,...
这个状态是TCP连接关闭过程中的一个中间状态,体现了TCP的四次挥手(FIN-ACK-FIN-ACK)过程的一个阶段。 1. **TCP连接状态机** TCP连接有多种状态,包括LISTEN、SYN_SENT、SYN_RECEIVED、ESTABLISHED、CLOSE_WAIT...
CLOSE_WAIT是一个常见的TCP连接状态,指的是服务器端的连接在客户端关闭后还未释放的情况。这种情况经常出现于客户端主动断开连接,但服务器端没有正确关闭连接的情况下。这种情况可能会导致服务器端出现大量未释放...
TIME_WAIT状态是TCP连接的四次挥手关闭协议中的一个重要状态,它存在的理由是为了确保TCP全双工连接的正常终止和避免老的重复分节在网络中消逝。 在TIME_WAIT状态中,客户端必须维持状态信息,以便在最后的ACK丢失...
TCP TIME_WAIT状态是TCP连接生命周期中的一个重要阶段,它发生在主动关闭连接的一方(通常称为客户端)在连接关闭后等待一段时间,以确保所有在网络中可能残留的数据片段都被接收并确认。这个阶段的存在是为了避免旧...
TIME_WAIT状态是TCP连接生命周期的一部分,用于确保数据传输的可靠性,但过多的TIME_WAIT连接会消耗系统资源,特别是端口资源。 TIME_WAIT状态的产生主要有两个原因: 1. Nginx作为负载均衡器,与PHP-FPM通信时通常...
在Linux系统中,进行网络编程时,TCP连接的TIME_WAIT状态是至关重要的一个环节。TIME_WAIT状态是TCP连接生命周期中的最后一个阶段,对于理解和优化网络应用性能有着直接的影响。本资源"TIME_WAIT.rar"包含了关于这个...
Linux内核协议栈中的TCP协议在处理连接关闭时,会进入一个特定的状态叫做time_wait。这个状态对于确保TCP连接的可靠性和避免旧连接与新连接混淆至关重要。在time_wait状态下,连接不会立即关闭,而是等待一段时间,...
在Linux服务器环境中,当TCP/IP连接关闭后,服务器端的端口可能会进入TIME_WAIT状态,这是TCP协议设计的一部分。TIME_WAIT状态的目的是确保网络中不存在旧的、可能重复的数据包,从而避免对新连接造成干扰,并确保...
本文讨论了在线上环境中,服务端长连接和客户端短连接配置不当导致Nginx服务器产生大量“TIME_WAIT”状态线程的问题,同时提供了问题的分析和解决方法。本文主要涉及的网络编程知识点包括长连接与短连接的定义和区别...
在TCP的生命周期中,TIME_WAIT是连接关闭阶段的一个重要状态。当TCP连接的一端(通常是客户端)发送了FIN(结束)报文并收到对方的ACK(确认)后,该连接就会进入TIME_WAIT状态。此状态的主要目的是确保所有的数据都...
这也是为什么有些人会建议调整sysctl.conf中的tcp_tw_reuse和tcp_tw_recycle参数,试图复用TIME_WAIT连接或快速回收它们。 然而,直接调整这些参数并不总是最佳解决方案,因为它们可能引入其他问题,比如导致连接...