在循环中调用 `accept` 系统调用会创建损坏的套接字

问题描述 投票:0回答:2

我正在尝试用 C 语言为自己构建一个 Web 应用程序。到目前为止,我已经让 HTTP 服务器正常工作了,但是由于某种原因,当我有需要其他 CSS 的 HTML 文件和 JavaScript 文件时,浏览器( Firefox)会非常快地执行多个请求,导致服务器和客户端之间的套接字连接损坏。我尝试了许多不同的方法来让 HTTP 服务器正常工作而不生成损坏的 TCP 连接。

目前,我有一个循环调用

accept
,并为每个新连接创建一个新线程:

for(;;)
{
    int newsockfd = accept(sockfd, (struct sockaddr *)&host_addr, (socklen_t *)&host_addrlen);
    if(newsockfd < 0)
    {
        sendf(stderr, LOG_ERROR, "Failed to accept connection...\n");
        continue;
    }
    pthread_t thread;
    if(pthread_create(&thread, NULL, handler, (void *)&newsockfd) != 0)
    {
        sendf(stderr, LOG_ERROR, "Failed to create a new thread...\n");
        return -4;
    }
}

我创建线程,同时将套接字文件描述符(表示客户端和服务器之间的新连接)发送到新线程。

处理新创建线程的函数如下所示:

void * handler(void * argument)
{
    int sockfd = *((int *)argument);

    struct sockaddr_in client_addr;
    int client_addrlen = sizeof(client_addr);

    int sockn = getpeername(sockfd, (struct sockaddr *)&client_addr, (socklen_t *)&client_addrlen);
    if(sockn < 0)
    {
        sendf(stderr, LOG_ERROR, "Failed to get client's address, got \x1b[35msockn\x1b[0m \x1b[33;3m%d\x1b[0m...\n", sockn);
        pthread_exit(NULL);
    }

    char * input = malloc(sizeof(char) * BUFFER_SIZE);

    int valread = read(sockfd, input, BUFFER_SIZE);
    if(valread < 0)
    {
        sendf(stderr, LOG_ERROR, "Failed to read from socket, got \x1b[35mvalread\x1b[0m \x1b[33;3m%d\x1b[0m...\n", valread);
        free(input);
        pthread_exit(NULL);
    }

    char * method = malloc(sizeof(char) * BUFFER_SIZE);
    char * url = malloc(sizeof(char) * BUFFER_SIZE);
    char * version = malloc(sizeof(char) * BUFFER_SIZE);
    sscanf(input, "%s %s %s", method, url, version);
    sendf(stdout, LOG_DEBUG, "Client \x1b[33;3m%s:%u\x1b[0m has sent a \x1b[33;3m%s\x1b[0m request to \x1b[33;3m%s\x1b[0m!\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), method, url);

    ...

当连接损坏时,我得到:

[ERROR]: Failed to get client's address, got sockn -1...

使用一些调试工具,然后检查并发现

method
url
verison
中的第一个字节设置为
NULL
。函数
getpeername
也返回 -1。 函数
sendf
只是基本的类似 printf 的函数,但似乎不是问题的原因。

有人发现这里有什么问题吗?

c http networking pthreads webserver
2个回答
1
投票

newsockfd
是循环的本地变量,您将指向它的指针发送到线程,但该指针在循环结束时(紧随
pthread_create
之后)将无效。

for(;;)
{
    int newsockfd = accept(...);
    pthread_create(..., (void *)&newsockfd);
} // <- here &newsockfd is a "dangling" pointer

因此在线程中

int sockfd = *((int *)argument);
不能保证工作。

您需要永久存储套接字描述符,以便指向它的指针保持有效 - 或者通过不让它超出范围来同步对它的访问,直到线程确认它已读取该值。

另一种解决方法是将

int
转换为
void*
并通过这样做将文件描述符按值传递给
pthread_create

pthread_create(..., (void *)newsockfd);

void *handler(void * argument) {
    int sockfd = (int)argument;
    //
}

0
投票

您正在向线程处理程序传递一个指向局部变量的指针,当线程开始运行时,该变量不再在范围内。

尝试将 newsockfd

value
传递给处理程序,而不是向其传递 pointer,例如:

int newsockfd = ...;
...
pthread_t thread;
pthread_create(..., (void *)newsockfd)
void * handler(void * argument)
{
    int sockfd = (int)argument;
    ...
}
© www.soinside.com 2019 - 2024. All rights reserved.