【发布时间】:2019-09-17 20:07:48
【问题描述】:
我正在编写我认为是一个简单的 C 套接字程序的代码。该代码将充当原始服务器。它需要创建一个监听socket,然后每次联系到它都需要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: Invalid argument”,这显然不好。
我从学生时代就做过一些简单的套接字编程,但我以前从未遇到过 accept() 的问题。我真的很困惑。在我看来,N-1 个请求到达并由 accept() 成功处理。但随后出现第 N 个,accept() 不喜欢它,并且套接字被破坏。如何或为什么,我不知道。
accept() 抱怨“无效参数”。但是客户端实际上是一遍又一遍地发送完全相同的测试消息。 N-1 个连接如何提供有效参数,但第 N、N+1、N+2... 个参数无效?这让我大吃一惊。
一些观察:
这是一种黑白现象。我的意思是 套接字在 100% 的时间里都能正常工作,但随后出现了问题 accept() 发生,然后套接字再也无法工作。
我进行了很多测试,但不知道有多少成功 当我开始一个新的测试时,我会得到连接。我有多达88个 在套接字停止工作之前成功连接。在另一个 测试,在套接字消失之前我只有 2 个成功的连接 仰卧起坐。之前的平均成功连接数 套接字中断时间约为 17。
我的客户端测试程序不会尝试将请求间隔 发送;它只是打开一个套接字并开始轰炸我的服务器 以最快的速度编程。我这样做是因为我想进行压力测试。 但也许我的客户端程序发送测试请求太快了 服务器要跟上……?
我想,也有可能是客户端程序导致 问题。我不这么认为,因为它只是发送相同的测试 一遍又一遍的消息,它永远不会引发错误。
有人见过这样的问题吗?我已经在 SO 上寻找了大约一个小时,但我找不到另一篇 accept() 有效的帖子,然后又无效了。
感谢任何帮助/建议。
【问题讨论】:
-
旁注:不要以 root 身份运行您的东西...使用用户帐户。
-
@DanielJour 是的,这很好。很好的收获,一定会做到的。 :)
标签: c sockets stress-testing