【问题标题】:C/C++: socket() creation fails in the loop, too many open filesC/C++:socket() 创建在循环中失败,打开的文件太多
【发布时间】:2020-05-17 10:41:02
【问题描述】:

我正在实现一个客户端-服务器 TCP 套接字应用程序。 Client 位于 OpenWRT Linux 路由器(基于 C)上,并以某种频率重复在套接字上写入一些数据并循环写入。 Server在Linux Ubuntu机器上(基于C/C++),根据数据到达速度循环读取数据。

问题:运行Server,然后运行Client,服务器不断读取新数据。双方工作良好,直到数据传递的数量(连接数)达到 1013。之后,Client 卡在socket(AF_INET,SOCK_STREAM,0)socket creation failed...: Too many open files。显然,在客户端打开fd 的数量接近ulimit -n = 1024

我放了代码的 sn-ps,它显示了 Server.cppClient.c 的循环结构:

服务器.c:

//    TCP Socket creation stuff over here (work as they should):
//    int sock_ = socket() / bind() / listen()

while (1)
{
        socklen_t sizeOfserv_addr = sizeof(serv_addr_);
        fd_set set;
        struct timeval timeout;
        int connfd_;
        FD_ZERO(&set);
        FD_SET(sock_, &set);
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        int rv_ = select(sock_ + 1, &set, NULL, NULL, &timeout);
        if(rv_ == -1){
            perror("select");
            return 1;
        }
        else if(rv_ == 0){
            printf("Client disconnected.."); /* a timeout occured */
            close (connfd_);
            close (sock_);
        }
        else{
            connfd_ = accept (sock_,(struct sockaddr*)&serv_addr_,(socklen_t*)&sizeOfserv_addr);
            if (connfd_ >= 0) {
                int ret = read (connfd_, &payload, sizeof(payload));    /* some payload */
                if (ret > 0)
                     printf("Received %d bytes !\n", ret);
                close (connfd_);  /* Keep parent socket open (sock_) */
            }else{
                printf("Server acccept failed..\n");
                close (connfd_);
                close (stcp.sock_);
                return 0;
            }
        }
}

客户端.cpp:

while (payload_exist)               /* assuming payload_exist is true */
{
        struct sockaddr_in servaddr;
        int sock;
        if (sock = socket(AF_INET, SOCK_STREAM, 0) == -1)
            perror("socket creation failed...\n");

        int one = 1;
        int idletime = 2;
        setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &idletime, sizeof(idletime));
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = inet_addr("192.168.100.12");
        servaddr.sin_port = htons(PORT);  /* some PORT */
        if (connect (sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0){
            perror("connect failed...");
            return 1;
        }
        write(sock, (struct sockaddr*)&payload, sizeof(payload));  /* some new payload */
        shutdown(sock,SHUT_WR);

        bool serverOff = false;
        while (!serverOff){
             if(read(sock, &res, sizeof(res)) < 0){
                 serverOff = true;
                 close(sock); 
             }
        }
}

注意payload800 bytes,并且每次write 操作总是会完全传输。在int main() 下定义了这两个代码,客户端继续创建套接字并发送数据,另一方面,服务器接收所有并自动close() 并在客户端终止时离开,由于使用select()。但是,如果我不终止Client,通过检查一些打印日志,很明显Server 在客户端因socket creation failed...: Too many open files 而崩溃之前成功接收了1013 个有效负载。

更新

按照 Steffen Ullrich 提到的观点,事实证明,客户端套接字 fd 没有泄漏,并且在原始循环中存在第二个 fd(保持打开状态)正在使 @987654345 @超出限制。

【问题讨论】:

  • 我确定close 总是会关闭 Linux 上的套接字。
  • 您应该在调用close() 时检查返回码。
  • 你确定它没有关闭袜子吗? close(sock); does 是否有可能关闭 sock 但那行代码实际上并没有运行?也许connect 失败了,因此套接字没有关闭?如果服务器正常关闭,最后一个循环(serverOff)将是一个无限循环,因为 read 返回 0。
  • ^^^^ 上面肯定会泄露一个描述符。如果connect 失败,你就盲目地返回,留下一个打开的套接字描述符。
  • @user253751 @WhozCraig,假设服务器永远不会首先终止,而是客户端。用户将终止Client,这将使Server 在超时后关闭。好吧,在 1013 条消息之后,我再也没有收到 perror(connect failed) 的回报。它只返回perror(socket creation failed)。我认为connect() 在客户端工作得很好。

标签: c++ c sockets select


【解决方案1】:
        if(read(sock, &res, sizeof(res)) < 0){
             serverOff = true;
             close(sock);   /********* Not actually closing sock *********/
        }

您对连接结束的检查是错误的。 read 在对方关闭连接时返回 0,&lt;0 仅在出错时返回。

    if (sock = socket(AF_INET, SOCK_STREAM, 0) == -1)
        perror("socket creation failed...\n");

鉴于 C 中的 precedence of operators,这基本上是说:

    sock = ( socket(AF_INET, SOCK_STREAM, 0) == -1) )
    if (sock) ...

假设socket(...) 不会返回错误而是文件描述符(即&gt;=0),则比较结果为假,因此如果socket 返回的fd 为&gt;0.

【讨论】:

  • 我已经尝试将=&lt; 作为read(sock, &amp;res, sizeof(res)) &lt;= 0),但结果没有任何变化。当我打印 read 时,它显示 -1 。那个小的while 循环总是会成功通过,否则它将永远卡在那个循环中。
  • @zlg:在这种情况下应该调用close。您确定在您的代码中某处没有使用其他文件描述符并且这些实际上正在泄漏(您没有显示完整的代码,所以无法分辨)?正如评论中所说,如果connect 失败,肯定会泄漏。
  • @zlg:您的代码还有更多问题,请参阅更新后的答案。或者您显示的代码不是您使用的实际代码,在这种情况下,任何试图找出其中问题的尝试都是无用的。
  • 这实际上是一个好点。没有其他套接字,但我还在整个代码中处理了一个日志(即 1400 行),并且应该仔细检查该单个文件上的所有 fopen fclose 匹配项。但是,即使发生这种情况,它是否一定会成为socket() 创作中的问题?
  • @zlg:对套接字没有限制,但对文件描述符一般没有限制。套接字和常规文件、管道、目录句柄 .. 都是文件描述符并受此限制。例如使用lsof -p process_id,您可以查看当前正在使用的文件描述符以及它们的用途。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-02
  • 2011-08-02
  • 1970-01-01
相关资源
最近更新 更多