【问题标题】:How to Solve Too Many Files Open Error from Socket Program without Setting ulimit -n如何在不设置ulimit -n的情况下解决Socket程序打开文件过多错误
【发布时间】:2018-06-12 09:45:44
【问题描述】:

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

在客户端被调用大约 1000 次之前一切正常,并且由于打开文件过多错误而崩溃。我发现原因是ulimit -n的设置,默认值为1024。

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

  1. 这可能意味着我的代码中存在错误
  2. ulimit 是为了保护我们的机器,将其设置得太高就像禁用保护一样
  3. 仍有机会达到限制,假设服务器运行数周或数月,问题仍未解决

我在网上尝试了一些套接字示例,但它们也有类似的问题,例如here

问题:

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

谢谢。

我的工作代码如下:

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;
}

顺便说一句,这只发生在服务器以 bash 运行时。如果服务器在 Eclipse 调试器中运行,它可以超过 1024 次调用。我不知道为什么。

【问题讨论】:

  • 在服务器中,您没有从您接受的客户端关闭套接字
  • 在你正在做的客户端sizeof(message_sent) 它将返回std::string 对象的大小而不是它代表你想要做的数据我认为是message_sent.size()
  • @Tyker 谢谢。你的意思是把 close(socket_fd_server);在循环内?我试过了,它肯定不会一直打开文件,但它不会一直循环运行,有什么提示吗?
  • 循环中的 close(socket_fd_server); 会失败,因为关闭套接字后您无法使用套接字,但不使用 socket_fd_client 后循环中的 close(socket_fd_client); 应该可以工作
  • @Tyker 非常感谢!这解决了问题。您介意回答一下,以便我接受吗?顺便说一句,既然我们在 server.cpp 中 close(socket_fd_client);,那么 client.cpp 中的 close(socket_fd); 是多余的,因此可以删除吗?

标签: c++ sockets ulimit


【解决方案1】:

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

【讨论】:

  • 谢谢你的建议,我去看看:)
【解决方案2】:

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

  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
  }

【讨论】:

    猜你喜欢
    • 2013-07-22
    • 1970-01-01
    • 2020-11-09
    • 2010-11-04
    • 1970-01-01
    • 2014-12-12
    • 1970-01-01
    • 2022-01-25
    • 2013-05-11
    相关资源
    最近更新 更多