如何解决太多文件打开套接字程序错误而不设置ulimit -n

时间:2018-06-12 09:45:44

标签: c++ sockets ulimit

我目前正在使用C ++编写套接字服务器和客户端。我的目标是让服务器保持运行,而客户端应该在发送和接收来自服务器的消息时终止。

一切正常,直到客户端被调用大约1000次,并且崩溃时打开的文件太多错误。我发现原因是 ulimit -n 的设置,其默认值为1024.

我找到的大多数解决方案都告诉我们设置更高的ulimit值。但是,我不确定这是否合适:

  1. 这可能意味着我的代码中存在错误
  2. ulimit是为了保护我们的机器,将它设置得太高就像禁用保护一样
  3. 仍有机会达到极限,假设服务器运行数周或数月,问题仍未解决
  4. 我在网上尝试了一些套接字示例,但它们也存在类似问题,例如here

    问题:

    1. 有什么办法可以避免这个错误吗?代码有什么问题吗?
    2. 设置ulimit -n是一个有害的高值,比如1048576?
    3. 谢谢。

      我的工作代码如下:

      server.cpp:

      #include <iostream>
      #include <string.h>
      #include <sys/socket.h> //sockaddr
      #include <sys/un.h> //for sockadder_un
      #include <errno.h>
      #include <unistd.h> //unlink
      #include <signal.h> //for catching ctrl+c
      
      bool keep_running = true;
      void terminate(int i) {
        keep_running = false;
      }
      
      int main(int argc, char** argv[]) {
      
        //--catch ctrl+c signal
        struct sigaction sigIntHandler;
        sigIntHandler.sa_handler = terminate;
        sigemptyset(&sigIntHandler.sa_mask);
        sigIntHandler.sa_flags = 0;
        sigaction(SIGINT, &sigIntHandler, NULL);
      
        //--create socket
        int socket_fd_server = socket(AF_UNIX, SOCK_STREAM, 0);
        if (socket_fd_server >= 0) {
          std::cout << "Server socket created" << std::endl;
        }
        else {
          std::cout << "Server socket failed to create, errno: " << errno << std::endl;
        }
      
        //--bind server
        sockaddr_un address_server;
        address_server.sun_family = AF_UNIX;
        strcpy(address_server.sun_path, "./socket-server");
        unlink(address_server.sun_path); //delete socket file if exists
      
        //will create a socket file, if already exist, errno=98
        if (bind(socket_fd_server, (sockaddr*)&address_server, sizeof(address_server)) >= 0) {
          std::cout << "Server bound" << std::endl;
        }
        else {
          std::cout << "Server failed to bind, errno: " << errno << std::endl;
          return 1;
        }
      
        //--mark socket as passive socket (listen)
        if (listen(socket_fd_server, 100) >= 0) {
          std::cout << "Start to listen" << std::endl;
        }
        else {
          std::cout << "Failed to listen, errno: " << errno << std::endl;
        }
      
        //--
        sockaddr_un address_client;
        char message_sent[] = {"message from server to client"};
        char message_received[100] = {};
        while (keep_running) {
          unsigned int len = sizeof(address_client);
          int socket_fd_client = accept(socket_fd_server, (sockaddr*)&address_client, &len);
          send(socket_fd_client, message_sent, sizeof(message_sent), 0);
          std::cout << "Server sent message: " <<message_sent << std::endl;
          recv(socket_fd_client, message_received, sizeof(message_received), 0);
          std::cout << "Server received message: " <<message_received << std::endl;
        }
        std::cout << "Leave while loop" << std::endl;
        unlink(address_server.sun_path);
        close(socket_fd_server);
        return 0;
      }
      

      client.cpp:

      #include <iostream>
      #include <string.h>
      #include <sys/types.h> //see NOTES in http://www.linuxhowtos.org/manpages/2/connect.htm
      #include <sys/socket.h> //sockaddr
      #include <sys/un.h> //for sockadder_un
      //#include <netinet/in.h> //for sockadder_in
      #include <unistd.h> //close
      #include <errno.h>
      
      int main(int argc, char** argv[]) {
      
        //--create socket
        int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
        if (socket_fd >= 0) {
          std::cout << "Client socket created" << std::endl;
        }
        else {
          std::cout << "Client socket failed to create, errno: " << errno << std::endl;
        }
      
        //--connection
        sockaddr_un address_un;
        address_un.sun_family = AF_UNIX;
        //address_un.sun_path = "./SocketServer";
        strcpy(address_un.sun_path, "../SocketServer/socket-server");
        if (connect(socket_fd, (sockaddr*)&address_un, sizeof(address_un)) == 0) {
          std::cout << "Client connected" << std::endl;
        }
        else {
          std::cout << "Client failed to connect, errno: " << errno << std::endl;
        }
      
        //--send message
        //char message_sent[] = {"test send message"};
        std::string message_sent = "test send message";
        if (send(socket_fd, message_sent.c_str(), sizeof(message_sent), 0) > 0) {
          std::cout << "Message: " << message_sent << " sent" << std::endl;
        }
        else {
          std::cout << "Message: " << message_sent << " not sent, errno: " << errno << std::endl;
        }
      
        //--receive message_sent
        char message_received[100] = {}; // = "test receive message";
        if (recv(socket_fd, message_received, sizeof(message_received), 0) > 0) {
          std::cout << "Message: " << message_received << " received" << std::endl;
        }
        else {
          std::cout << "Message: " << message_received << " not received, errno: " << errno << std::endl;
        }
      
        //--close
        close(socket_fd);
      
        return 0;
      }
      

      BTW,这只发生在服务器以bash运行时。如果服务器在Eclipse调试器中运行,则可能超过1024个调用。我不知道为什么。

2 个答案:

答案 0 :(得分:2)

你正在泄漏套接字。尝试一些错误检查。这里还不够。如果recv()返回零,请关闭套接字。如果它返回-1且errno不是EAGAIN/EWOULDBLOCK,请关闭套接字。如果send()返回-1,请关闭套接字。

答案 1 :(得分:1)

在服务器中,您没有关闭您接受的客户端的文件描述符

  while (keep_running) {
    unsigned int len = sizeof(address_client);
    int socket_fd_client = accept(socket_fd_server, (sockaddr*)&address_client, &len);
    send(socket_fd_client, message_sent, sizeof(message_sent), 0);
    std::cout << "Server sent message: " <<message_sent << std::endl;
    recv(socket_fd_client, message_received, sizeof(message_received), 0);
    std::cout << "Server received message: " <<message_received << std::endl;
    close(socket_fd_client); //close the file descriptor
  }