【问题标题】:Socket connection refused due to a burst of connections套接字连接因连接突发而被拒绝
【发布时间】:2016-09-21 20:42:54
【问题描述】:

我正在使用套接字来同步多个远程进程。

这个想法是一个进程创建一个管理服务器端的 pthread,就像这样:

void *listener(void * in) {
  int sockfd;
  socklen_t clilen;
  struct sockaddr_in serv_addr, cli_addr;
  int n = *((int *) in);

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0)
      error("ERROR opening socket");

  int option = 1;
  setsockopt(sockfd, SOL_SOCKET, (SO_REUSEPORT | SO_REUSEADDR), (char*) &option, sizeof (option));
  bzero((char *) &serv_addr, sizeof (serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(PORT);

  if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0)
      error("ERROR on binding");

  if (listen(sockfd, n) < 0)
      error("ERROR when listening");

  clilen = sizeof (cli_addr);
  int cnt = 0;
  while (cnt < n) {
      int newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
      if (newsockfd < 0) {
          error("ERROR on accept");
      }
      cnt++;
  }
  close(sockfd);
  return 0;
}

同时,其他进程将执行:

int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
  error("ERROR opening socket");

server = gethostbyname(_managementHost); //managementHost);
if (server == NULL)
  error("ERROR, no such host\n");

bzero((char *) &serv_addr, sizeof (serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *) server->h_addr, (char *) &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(PORT);

if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0)
  error("ERROR connection");

close(sockfd);

现在,我遇到的问题是,当我有很多进程试图同时连接到服务器时,其中一些进程会抛出 connection denied 错误。

我猜那是因为接受可能还没有准备好......事实上,我已经读过它可能会发生,但我还没有找到我的具体情况。

有人能解释一下吗?

我想到的一个可能的解决方案是为每个接受创建一个线程,但我更愿意避免它。

谢谢。

编辑:更正了服务器中套接字的双重初始化。感谢@Remy Lebeau。

【问题讨论】:

  • 我认为我不太了解您要做什么。通过接受连接然后泄漏它们应该完成什么?这些如何产生任何同步?或者您是否删除了一些重要的部分来满足您的同步和非资源泄漏目标?
  • 这个想法是第一个进程将在pthread_join 等待,直到其他进程达到给定点。我只使用套接字来同步它们,我对进程通信不感兴趣。

标签: c sockets parallel-processing synchronization


【解决方案1】:

现在,我遇到的问题是,当我有很多进程试图同时连接到服务器时,其中一些进程会抛出连接被拒绝的错误。

正在侦听的 TCP 套接字有待处理的连接积压。 listen() 的第二个参数指定在被接受之前允许在积压队列中的连接数。如果新客户端在积压已满时尝试连接,则客户端将被拒绝。客户端或服务器对此无能为力。检测错误并在稍后重新连接是客户端的责任。

您的listener() 正在接受客户端,直到达到指定的连接数,但您也使用相同的数字作为侦听积压队列大小。活动连接数和待处理连接数是两个不同的东西。如果您希望大量客户端同时连接,则需要较大的 backlog 大小以避免拒绝错误。但是该积压的大小应该与您期望的流量成正比。如果您有 1000 个客户端,但它们一次只连接 20 个,您可以将积压工作设置为 25 个,而不是 1000 个。

您的listener() 有一些其他逻辑错误。它调用socket() 两次并将两个套接字保存到同一个sockfd 变量,因此它泄漏了第一个套接字。您需要删除对socket() 的第二个调用(setsockopt() 之前的那个)。您还泄漏了accept() 返回的套接字。使用后需要close() 接受的套接字。

【讨论】:

  • 所以,即使进程连接并且accept() 尚未发出,客户端连接仍将保留在积压中,对吗?关于错误,你是对的,我有太多的socket() 电话。但是,我无法在accept() 之后关闭套接字,因为我只打开了一个套接字(它只有一个执行listener() 的线程。我应该为每个accept() 创建一个线程吗?谢谢。
  • @SergioIserte 是的,连接保留在积压中,直到被accept() 删除。如果您打算一次为多个客户端提供服务,则必须为每个接受的客户端创建一个新线程/进程(而不是每次调用 accept() 本身),或者在一个线程中多路复用多个客户端(例如使套接字非-blocking,然后使用select()/epoll()来管理它们),所以你可以定期回到accept()
  • 抱歉,我不确定您对接受的客户的意思。我指的是创建n 线程,每个线程都调用accept()。你想说什么?感谢您的宝贵时间。
  • @SergioIserte 连接在accept() 返回时被“接受”。您不能从n 线程调用accept(),它只能由一个线程调用。您需要接受一个客户并为该客户委派工作负载,然后返回accept() 接受下一个客户。要么 1) 在循环中调用 accept() 并将每个客户端传递给另一个线程,或者 2) 使用 select()/epoll() 来监视多个套接字,以便在何时调用 accept()/recv()/ 时得到通知send() 给他们。这应该包含在任何像样的插座书/教程中。
  • 好的,现在我明白了。但是,在我的问题中详细说明了您的第一个选项(除了以后的通信,我没有),它返回一个 connection denied。此外,只要我不需要它,我就会关闭套接字(就在所有accepts() 已连接之后。我们又开始了:(谢谢。
【解决方案2】:

只需通过调用 fork 为每个客户端生成一个子节点来使您的服务器并发。最简单的方法,IMO。避免线程化并停止连接被拒绝错误。

编辑:您还可以考虑预先分叉您的服务器。不过,您必须研究如何处理围绕接受的锁定(如果您完全锁定)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    • 2015-03-24
    • 1970-01-01
    相关资源
    最近更新 更多