套接字在 32739 次连接后不会关闭

Posted

技术标签:

【中文标题】套接字在 32739 次连接后不会关闭【英文标题】:sockets go to not closing after 32739 connections 【发布时间】:2013-07-12 23:33:03 【问题描述】:

更新:在进一步调查 lil 之后,我发现了这种行为的真正问题。问题是,我正在为每个连接创建线程并将 sock fd 传递给线程,但没有立即 pthraed_joining,这样我的主线程就无法在连接接受后创建更多线程。我关闭套接字的逻辑是在子线程中,因为我无法关闭套接字,因此他们将进入等待关闭状态。所以我只是在创建线程后将它们分离,现在一切正常!!

我有一个客户端服务器程序,我正在使用脚本来运行客户端并建立尽可能多的连接并在发送一行数据并退出客户端后关闭它们,一切正常,直到第 32739 个连接,即连接两边都关闭了,但在那个数字之后,连接没有关闭,服务器停止接收更多连接,如果这样做

netstat -tonpa 2>&1 | grep CLOSE

我看到大约 1020 个套接字等待关闭。从命令中采样,

tcp 25 0 192.168.0.175:16099 192.168.0.175:41704 CLOSE_WAIT 5250/./bl_manager off (0.00/0/0)
tcp 24 0 192.168.0.175:16099 192.168.0.175:41585 CLOSE_WAIT 5250/./bl_manager off (0.00/0/0)
tcp 30 0 192.168.0.175:16099 192.168.0.175:41679 CLOSE_WAIT 5250/./bl_manager off (0.00/0/0)
tcp 31 0 192.168.0.175:16099 192.168.0.175:41339 CLOSE_WAIT 5250/./bl_manager off (0.00/0/0)
tcp 25 0 192.168.0.175:16099 192.168.0.175:41760 CLOSE_WAIT 5250/./bl_manager off (0.00/0/0)

我正在使用以下代码来检测客户端断开连接。

for(fd = 0; fd <= fd_max; fd++) 
    if(FD_ISSET(fd, &testfds)) 
       if (fd == client_fd) 
           ioctl(fd, FIONREAD, &nread);
           if(nread == 0) 
               FD_CLR(fd, &readfds);
               close(fd);
               return 0;
           
       
    
 /* for()*/

如果我做错了什么,请告诉我。它是一个 Python 客户端和 CPP 服务器设置。

谢谢你

【问题讨论】:

你在哪个平台上?另外,为什么这被标记为 Python?您是否有任何理由相信客户端正在使用套接字做一些不正当的事情,从而阻止您的服务器工作? 是的,我对此表示怀疑! 【参考方案1】:

CLOSE-WAIT 表示端口正在等待本地应用程序关闭套接字,已经收到对等方的关闭。显然,您以某种方式泄漏了套接字,可能在错误路径中。

“检测客户端断开连接”的代码完全不正确。您正在测试的只是可以在没有阻塞的情况下读取的数据量,即已经到达的数据量。正确的测试是 recv() 的返回值为零或读取或写入时出现 EAGAIN/EWOULDBLOCK 以外的错误。

【讨论】:

这不是ioctl用来查看socket中有没有数据可以读取的,没有实际读取。据我所知,如果套接字在另一侧关闭,它会返回 0,如果我的理解有误,请告诉我。 您的“理解”是错误的,确实毫无根据。再次阅读我写的内容,或查看 FIONREAD 的文档:“返回可立即读取的字节数”、“返回输入缓冲区中的字节数”等等。没有一个关于对等点在任何地方断开连接的消息. ***.com/questions/283375/… 如果这是错误的,我们绝对应该告诉上面线程中的那些人。但对我来说,它按预期正常工作!它检测到客户端更接近的连接。 不,它没有。它检测何时没有数据可以读取而不阻塞。如果客户在五分钟内没有给你发送任何东西,那不是断开连接,是吗?请提供主要来源:*** 上的任何内容都无法覆盖我在此处引用的实际文档。您不能在反驳官方文件时引用 SO 答案。正如我现在在评论中所说,您引用的线程中接受的答案与您一样非常不正确。【参考方案2】:

在不知道你的平台的情况下,我无法确定,但事实上你显然在使用select,而且你在距离 32768 仅几十个地方遇到问题,这似乎很可能是你的问题。

fd_set 是位的集合,由文件描述符编号索引。每个平台都有不同的最大数量。 OpenBSD 和最新版本的 FreeBSD 和 OS X 通常将 fd_set 限制为 FD_SETSIZE,默认为 1024。不同的 linux 机器似乎有 1024、4096、32768 和 65536。

那么,如果 FD_ISSET(32800, &amp;testfds)FD_SETSIZE 是 32768,会发生什么?你要求它从任意内存中读取一点。

select 或在此之前的其他调用应该在您为 nfds 参数传入 32800 时给您一个 EINVAL 错误……但从历史上看,许多平台都没有这样做。或者他们返回了一个错误,但只有在正确填写第一个 FD_SETSIZE 位并将其余部分设置为未初始化的内存之后,这意味着如果您忘记检查错误,您的代码似乎可以工作,直到您强调它。

这是对超过几百个套接字使用select 的原因之一是一个坏主意。另一个原因是select 是线性的(更糟糕的是,当前 套接字的数量不是线性的,而是最高 fd 的线性,所以即使在大多数客户端离开后它仍然很慢)。

大多数具有select 的现代平台也具有poll,从而避免了这个问题。

除非您使用的是 Windows……在这种情况下,不使用 select 的完全不同理由和不同的答案。

【讨论】:

非常感谢您的回复!在调查了更多之后,我发现我的概率不同的原因。我完全明白你所说的,我也有同样的想法。但问题是这些线程是如此之短,以至于它们在几毫秒内关闭套接字并死掉。所以套接字 fd 被重用。我想我已经找到了解决问题的方法,我会尝试更新问题!

以上是关于套接字在 32739 次连接后不会关闭的主要内容,如果未能解决你的问题,请参考以下文章

发送后如何正确关闭套接字(使用 IOCP)?

为啥我的 TCP 服务器套接字在一个客户端断开连接后关闭?

为啥在从服务器接收到所有数据后客户端套接字连接没有关闭?

c++ 服务器在客户端终止连接进程后不关闭 TCP 套接字连接

WSA UDP 套接字无法重用,因为它强制关闭连接

通过select监控多个描述符实现并发连接