C Programming :: accept() 在压力测试中工作然后不工作

C Programming :: accept() works and then doesn't, in stress-testing

我正在编写我认为是简单的 C 套接字程序的代码。该代码将充当原始服务器。它需要创建一个监听套接字,然后每次联系时,它需要accept()传入的连接,然后衍生出一个线程来处理一些基本的处理。我或多或少的线程和处理部分在工作,但是 accept() 部分在代码成功处理一些初始连接后出现故障。这是一个问题,因为我预计该程序将需要 运行 持续运行,并且可能必须处理大量传入请求。

首先是一些环境方面的东西:我正在开发一个 Ubuntu 盒子,我的代码是用 GCC 编译的:

root@ubuntu:/home/me/socketProject# gcc -v
...
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
root@ubuntu:/home/me/socketProject#

我的 main() 函数设置一个监听套接字,然后开始一个无限循环。对于进入的每个连接,它都会尝试 accept() 然后将工作传递给分离的线程:

int main(int argc, char ** argv)
{
   int             sock = -1;
   struct          sockaddr_in address;
   int             port = 12345;
   connection_t*   connection;
   pthread_t       thread;

   // Create the listening socket
   sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (sock <= 0){
      fprintf(stderr, "%s: error: cannot create socket\n", argv[0]);
      return -3;
   }

   // Bind socket to port
   address.sin_family = AF_INET;
   address.sin_addr.s_addr = INADDR_ANY;
   address.sin_port = htons(port);
   if (bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0){
      fprintf(stderr, "%s: error: cannot bind socket to port %d\n", argv[0], port);
      return -4;
   }

   // Listen on the port
   if (listen(sock, 5) < 0){
      fprintf(stderr, "%s: error: cannot listen on port\n", argv[0]);
      return -5;
   }

   // Listen forever...
   while (1){
      // Accept incoming connections
      connection = (connection_t *)malloc(sizeof(connection_t));

      printf("1)  [[ sock: %d -- address: %s -- addr_len: %d ]]\n", connection->sock, &connection->address.sa_data, connection->addr_len );

      connection->sock = accept(sock, &connection->address, &connection->addr_len);

      printf("2)  [[ sock: %d -- address: %s -- addr_len: %d ]]\n", connection->sock, &connection->address.sa_data, connection->addr_len );

      if (connection->sock <= 0){
         printf(" *** PROBLEM!!!  Value of errno: %d\n ", errno);
         free(connection);
      }
      else{
         // Start a new thread but do not wait for it
         pthread_create(&thread, 0, process, (void *)connection);  // process() omitted
         pthread_detach(thread);
      }
   }
   return 0;
}

漂亮的教科书内容。当只有一个传入连接时,我制定了所有逻辑并使一切正常工作。

但后来我开始进行压力测试,并立即意识到我遇到了问题。我编写了一个客户端程序,它连续发送 X 个测试请求。上面的服务器代码对于一些初始请求工作正常,但随后就崩溃了。这是输出:

root@ubuntu:/home/me/socketProject# ./server
        *** 1)  [[ sock: 18954128 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 7 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18970816 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 8 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18962080 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 9 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18962112 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 10 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18954064 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 11 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18954160 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 12 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18907120 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 13 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18950880 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 14 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 15 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18957136 -- address:  -- addr_len: 0 ]]
        *** 2)  [[ sock: 17 -- address:  -- addr_len: 16 ]]
        *** 1)  [[ sock: 18957168 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]
        *** 2)  [[ sock: -1 -- address:  -- addr_len: -518793662 ]]
 *** PROBLEM!!!  Value of errno: 22
        *** 1)  [[ sock: 18961856 -- address:  -- addr_len: -518793662 ]]

你看到问题了。在上面的例子中,大约十次成功请求后发生了一些事情,然后 accept() 开始抱怨 ERRNO 22。快速 Google 搜索显示 ERRNO 22 是“EINVAL:无效参数”,这显然不好。

我从学生时代开始就做过一些简单的套接字编程,但我以前从未遇到过 accept() 的问题。我真的很纳闷。在我看来,N-1 个请求到达并被 accept() 成功处理。但随后第 N 次出现,accept() 不喜欢它,套接字被破坏。怎么样,为什么,我不知道。

accept() 正在抱怨 "invalid argument(s)"。但是客户端实际上是一遍又一遍地发送完全相同的测试消息。 N-1 连接如何提供有效参数,但第 N、N+1、N+2... 参数无效?这让我大吃一惊。

一些观察:

有没有人见过这样的问题?我一直在寻找 SO 大约一个小时,但我找不到另一个 post 其中 accept() 工作,然后没有。

任何 help/advice 不胜感激。

您需要将 addr_len 初始化为 addr 的大小。

如果您查看 accept 的联机帮助页,在列出的 errno 值下; EINVAL 如果 addrlen 无效(例如负数)

(吹嘘):这不是相同的论点。

您正在调用 malloc(),这可能 return 未初始化内存。

无论您设置什么值addrlen 可以将地址的多少字节复制到地址中; 0 表示 none.