如何正确处理套接字接受返回"太多打开的文件"

时间:2016-08-21 20:35:13

标签: c sockets

我在tcp端口上有一个侦听套接字。该进程本身使用setrlimit(RLIMIT_NOFILE,&...);来配置进程允许的套接字数。

对于测试,RLIMIT_NOFILE设置为20,当然对于生产,它将被设置为更大的数字。 20可以很容易地在测试环境中达到极限。

服务器本身没有描述符泄漏或类似的问题,但试图通过增加RLIMIT_NOFILE来解决问题显然是做不到的,因为在现实生活中无法保证不会达到限制,无论它有多高集。

问题是在达到限制accept后返回Too many open files并且除非关闭文件或套接字,否则事件循环会立即开始旋转,占用100%的一个核心。即使客户端关闭连接(例如由于超时),服务器也将循环,直到文件描述符可用于处理并关闭已经死的传入连接。 编辑:另一方面,客户端停滞不前,没有好办法知道服务器过载。

我的问题:在accept返回Too many open files后关闭传入连接是否有一些标准方法来处理这种情况。

想到几种肮脏的方法:

  • 关闭并重新打开侦听套接字,希望所有挂起的连接都将被关闭(这非常脏,因为在线程服务器中,某些其他线程可能会获取释放的文件描述符)
  • 跟踪打开的文件描述符计数(使用具有一些未跟踪的文件描述符的外部库无法正常完成)
  • 检查文件描述符号是否接近限制并在情况发生之前开始关闭传入连接(这是特定于实现的,虽然它可以在Linux上运行,但不能保证其他操作系统将在同一个文件描述符中处理方式)

编辑:另一种肮脏和丑陋的方法:

保留一个备用fd(例如dup(STDIN_FILENO)open("/dev/null",...))将在accept失败时使用。顺序将是:

... accept failed
// stop threads
close(sparefd);
newconnection = accept(...);
close(newconnection);
sparefd = open("/dev/null",...);
// release threads

这种方法的主要缺点是线程同步,以防止其他线程获得刚刚释放的备用fd。

3 个答案:

答案 0 :(得分:1)

您不应使用setrlimit来控制进程可以处理的并发连接数。你的一小部分套接字代码对整个应用程序的其余部分说:“我只希望一次打开N个连接,这是我知道怎么做的唯一方法,所以...没有别的这个过程可以有任何文件!“如果每个人都这样做会怎么样?

执行您想要的操作的正确方法很简单 - 跟踪您打开的连接数,并且在您可以处理另一个连接之前不要调用accept

答案 1 :(得分:1)

注意poll(2)之类的多路复用系统调用可以在accept - 套接字(以及连接的套接字或任何其他类型的流文件描述符)上工作(等待而不需要繁忙的自旋循环)。

所以只需让你的事件循环处理它们(可能还有其他可读和可写的文件描述符)。如果您不想,请不要致电accept(2)

答案 2 :(得分:1)

我知道你的代码在库中。库遇到资源限制事件。一般来说,我会区分灾难性事件(内存耗尽,无法打开侦听套接字)和可能是临时事件的事件。灾难性事件很难处理:没有记忆,甚至记录或有序关闭也许是不可能的。

相比之下,过多的打开文件可能是暂时的,尤其是因为我们是资源占用者。临时错误条件幸运地处理:通过等待。这是你不做的事情:你应该在accept返回“太多打开的文件”之后等待一个咒语,然后再打电话给accept。这将解决100%CPU负载问题。 (我假设我们的服务器在每个连接上执行一些工作,这些工作在某些时候已完成,因此我们库保存的客户端连接的文件描述符最终会被关闭。)

仍然存在库无法知道用户代码要求的问题。 (accept之间的暂停应该多长时间? 1 让连接请求完全等待是否可接受(原文如此)?我们是否会在某个时候放弃?)必须将错误报告回用户代码,以便用户代码有机会查看并修复错误。

如果用户代码返回文件描述符,那很容易:返回accept的错误代码(并确保记录这种可能性)。因此,我假设用户代码从未看到像文件描述符这样的粗略细节,而是获取一些数据,例如。甚至可能是库可能同时执行副作用,因此用户代码永远不会看到任何可用于传达错误的返回值。然后,库必须提供一些其他方式来向用户代码发送错误信号。这可能会对用户代码如何使用库施加限制:可能在某些函数调用之前或之后,或者只是定期,必须主动检查错误状态。

<小时/> 1 顺便说一下,即使在阅读accept手册页之后,我也不清楚客户端的connect是否失败(因为连接请求已被排除在服务器端,但无法处理),或者请求是否只是停留在队列中,以便客户端除了延迟之外不知道服务器的问题。