c - TCP 服务器 - 从 "Too many open files"恢复

标签 c sockets tcp

我正在用 C 编写一个 TCP 服务器,发现一旦监听 fd 出现“太多打开的文件”错误,就会发生一些不寻常的事情。 accept 调用不再阻塞并始终返回 -1。

我也试过关闭监听的fd,重新打开,重新绑定(bind),但是好像没有用。

我的问题是为什么 accept 在这种情况下一直返回 -1,我应该怎么做才能停止它 并使服务器能够在任何旧客户端关闭后接受新连接? (当某些连接关闭时,套接字当然能够再次正确地接受)

======更新:澄清======

问题的发生只是因为活跃客户端的数量超过了打开的fds的限制,所以我在示例代码中没有关闭任何接受的fd,只是为了让它重现更快。

我在每次accept返回输出时添加时间戳并将connect频率减慢到每2秒一次,然后我发现实际上“Too many open文件”错误发生在最近一次成功 accept 之后。所以我认为那是因为当达到maxium fds时,每次调用accept都会立即返回,返回值为-1。 (我认为 accept 仍然会阻塞,但在下一个传入的 connect 时返回 -1。在这种情况下 accept 的行为是我自己的理论,不是来自手册页。如果有误,请告诉我)。

所以对于我的第二个问题,为了让它停止,我认为这是一个在任何连接关闭d之前停止调用accept的解决方案。

同时更新示例代码。感谢您的帮助。

======示例代码======

我是这样测试的。首先将 ulimit -n 设置为一个较低的值(如 16),然后运行从以下 C 源代码编译的服务器程序;然后使用 Python 脚本创建多个连接

/* TCP server; bind :5555 */

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE 1024
#define PORT 5555

void error(char const* msg)
{
    perror(msg);
    exit(1);
}

int listen_port(int port)
{
    int parentfd; /* parent socket */
    struct sockaddr_in serveraddr; /* server's addr */
    int optval; /* flag value for setsockopt */
    parentfd = socket(AF_INET, SOCK_STREAM, 0);
    if (parentfd < 0) {
        error("ERROR opening socket");
    }

    optval = 1;
    setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR, 
           (const void *)&optval , sizeof(int));

    bzero((char *) &serveraddr, sizeof(serveraddr));

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons((unsigned short)port);

    if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
        error("ERROR on binding");
    }

    if (listen(parentfd, 5) < 0) {
        error("ERROR on listen");
    }
    printf("Listen :%d\n", port);
    return parentfd;
}

int main(int argc, char **argv)
{
    int parentfd; /* parent socket */
    int childfd; /* child socket */
    int clientlen; /* byte size of client's address */
    struct sockaddr_in clientaddr; /* client addr */
    int accept_count; /* times of accept called */

    accept_count = 0;
    parentfd = listen_port(PORT);

    clientlen = sizeof(clientaddr);

    while (1) {
        childfd = accept(parentfd, (struct sockaddr *) &clientaddr, (socklen_t*) &clientlen);
        printf("accept returns ; count=%d ; time=%u ; fd=%d\n", accept_count++, (unsigned) time(NULL), childfd);
        if (childfd < 0) {
            perror("error on accept");

            /* the following 2 lines try to close the listening fd and re-open it */
            // close(parentfd);
            // parentfd = listen_port(PORT);

            // the following line let the program exit at the first error
            error("--- error on accept");
        }
    }
}

创建连接的Python程序

import time
import socket

def connect(host, port):
    s = socket.socket()
    s.connect((host, port))
    return s

if __name__ == '__main__':
    socks = []

    try:
        try:
            for i in xrange(100):
                socks.append(connect('127.0.0.1', 5555))
                print ('connect count: ' + str(i))
                time.sleep(2)
        except IOError as e:
            print ('error: ' + str(e))
        print ('stop')
        while True:
            time.sleep(10)
    except KeyboardInterrupt:
        for s in socks:
            s.close()

最佳答案

why accept keeps returning -1 in this situation

因为您已经用完了文件描述符,就像错误消息所说的那样。

what am I supposed to do to stop it and make the server be able to accept new connections after any old clients closed?

关闭客户端。 问题 不是accept() 返回-1,而是您没有在完成处理后关闭已接受的套接字。

关闭监听套接字不是解决方案。这只是另一个问题。

编辑“完成他们”是指以下几件事之一:

  1. 他们已经完成了对你的处理,这由 recv() 返回零表示。
  2. 你已经完成了他们,例如发送最终回复后。
  3. 除了 EAGAIN/EWOULDBLOCK 之外,您在向他们发送或从他们那里接收错误时。
  4. 当您遇到一些其他内部 fatal error 阻止您进一步处理该客户端时,例如收到无法解析的请求,或其他一些使连接或 session 或整个客户端无效的致命应用程序错误.

在所有这些情况下,您应该关闭已接受的套接字。

关于c - TCP 服务器 - 从 "Too many open files"恢复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36784191/

相关文章:

c - 从命令行在 Eclipse 中构建项目配置

Java 套接字重用

vb.net - 如何处理TCPListener "An existing connection was forcibly closed by the remote host"

c++ - 展开 C 或 C++ 源文件中的单个宏

c - 无越界错误

c - 未知的 C 表达式

php - PHP的fSockOpen查询到服务器?

java - 无法将套接字设置为非阻塞

linux - Indy 10 以 1024 个 block 发送数据。如何增加 block 大小?

heroku - 使用 Ruppell 的 Sockets 插件在 Heroku 上托管 TCP 服务器时出现 "Connection closed by foreign host"错误