即使 poll() 说有消息也没有收到消息

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

我设置了一个基本的套接字连接,我已经为服务器编写了代码,我暂时只是使用 google chrome 和 localhost 来测试我的代码是否可以正确接收消息。我在recv()阻塞方面遇到了一些问题,所以我使用了poll(),正如我在网上看到的建议一样,并且在快速搜索了select()和poll()之间的差异之后。我的代码能够检索消息,但 poll 仍然认为有消息要读取,即使显然没有(因为 recv() 正在阻塞)。

这是我的简化代码:

/*
request is a pointer to a struct with the following members:
char *data;
int size;
*/
request->data = (char *)malloc(1);
char *data_ptr = request->data;
if (request->data == NULL)
{
    perror("Error allocating memory");
    return 8;
}
request->size = 1;
struct pollfd fds[1];
struct pollfd read;
read.fd = *connectionptr;
read.events = POLL_IN | POLL_PRI;
fds[0] = read;
int available_ops = poll(fds, 1, 0);
while (available_ops > 0)
{
    if (fds[0].revents & POLL_IN || fds[0].revents & POLL_PRI)
    {
        if (recv(*connectionptr, data_ptr, 1, 0) < 0)
        {
            perror("Error receiving data");
            return 6;
        }
        request->data = (char *)realloc(request->data, request->size + 1);
        request->size++;
        data_ptr = request->data + request->size - 1;
        if (request->data == NULL)
        {
            perror("Error allocating mem");
            return 8;
        }
    }
    available_ops = poll(fds, 1, 0);
    printf("Read %s thus far, %d characters more to read\n", request->data, available_ops);
}

运行此命令,我得到以下最终输出(最后一个打印语句,即运行的输出):

Read GET / HTTP/1.1
Host: 127.0.0.1:443
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

thus far, 1 characters more to read

在此之后,程序停止运行,通过检查调试器,我发现是recv(在上面的示例代码中,但为了方便起见,在下面再次显示)阻塞了流程。

// ... is indicating that there is stuff before/after it
...if (recv(*connectionptr, data_ptr, 1, 0) < 0)...

套接字配置了保持活动状态(我尝试禁用它,因为我认为这可能是问题所在,但它不起作用)和地址重用。

编辑:

以下是问题的完整代码(排除问题后的代码),因为我被告知这不足以满足可重现的需求:

#include "server.h"

int main(void)
{
    int socket_desc = socket(AF_INET6, SOCK_STREAM, 0);
    if (socket_desc < 0)
    {
        perror("Error initializing socket");
        close(socket_desc);
        return 1;
    }
    printf("Socket created\n");
    int reuse = 1;
    int keep_alive = 1;
    if (setsockopt(socket_desc, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        perror("Socket Address Reuse could not be set");
        close(socket_desc);
        return 2;
    }
    printf("Socket set to reuse address\n");
    if (setsockopt(socket_desc, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_alive)) < 0)
    {
        perror("Keep Alive could not be activated");
        close(socket_desc);
        return 3;
    }
    printf("Socket set to keep connection alive\n");
    struct sockaddr_in6 address;
    memset(&address, 0, sizeof(address));
    address.sin6_family = AF_INET6;
    address.sin6_port = htons(443);
    address.sin6_addr = in6addr_any;
    int binding = bind(socket_desc, (struct sockaddr *)&address, sizeof(address));
    if (binding < 0)
    {
        perror("Bind failed");
        close(socket_desc);
    }
    printf("Socket bound to port 443\n");
    if (listen(socket_desc, 5) < 0)
    {
        perror("Error in listening");
        close(socket_desc);
        return 4;
    }
    printf("Listening...\n");
    struct sockaddr client_info = {0};
    socklen_t client_info_length = sizeof(client_info);
    int soc_conn = accept(socket_desc, &client_info, &client_info_length);
    if (soc_conn < 0)
    {
        perror("Error accepting connection to client");

        close(socket_desc);
        return 5;
    }
    struct chararr receive_buffer;
    int code = process_request(&soc_conn, &receive_buffer);
    if (code != 0)
    {
        free(receive_buffer.data);
        close(socket_desc);
        close(soc_conn);
        return code;
    }

    printf("Message Sent!\n");
    printf("ending program...\n");
    close(socket_desc);
    close(soc_conn);
    free(receive_buffer.data);
    return 0;
}

int process_request(int *connectionptr, struct chararr *request)
{
    request->data = (char *)malloc(1);
    char *data_ptr = request->data;
    if (request->data == NULL)
    {
        perror("Error allocating memory");
        return 8;
    }
    request->size = 1;
    printf("Receiving Data\n");
    struct pollfd fds[1];
    struct pollfd readcheck;
    readcheck.fd = *connectionptr;
    readcheck.events = POLL_IN | POLL_PRI;
    fds[0] = readcheck;
    int available_ops = poll(fds, 1, 0);
    while (available_ops > 0)
    {
        
        if (fds[0].revents & POLL_IN || fds[0].revents & POLL_PRI)
        {
            if (recv(*connectionptr, data_ptr, 1, 0) < 0)
            {
                perror("Error receiving data");
                return 6;
            }
            request->data = (char *)realloc(request->data, request->size + 1);
            request->size++;
            data_ptr = request->data + request->size - 1;
            if (request->data == NULL)
            {
                perror("Error allocating mem");
                return 8;
            }
        }
        available_ops = poll(fds, 1, 0);
        printf("Read %s thus far, %d characters more to read\n", request->data, available_ops);
    }
    return 0;
}

服务器.h:

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <poll.h>

struct chararr
{
    char *data;
    int size;
};

int process_request(int *connectionptr, struct chararr *request);
c sockets recv
1个回答
0
投票

由于您没有提供可重现的示例,我不能说这个答案包含修复程序,但我看到了这些问题:

realloc()
可以返回不同的指针

您的代码像这样调用

realloc()

    request->data = (char *)realloc(request->data, request->size + 1);

但是,您还维护另一个变量,

data_ptr
,它指向您的缓冲区。

realloc()
可以返回与传递给它的指针不同的指针。所以你需要放弃你的
data_ptr
想法。相反,跟踪到目前为止已读取的字节数,并将其用作当前缓冲区指针的索引(可以通过
realloc()
更改)。

如果

data_ptr

 为您提供了不同的指针,则取消引用 
realloc()
 指针将调用未定义的行为。这很可能是您问题的根源。

一次增加一个字节的分配

这是极其低效的。您应该分配您认为在典型情况下可能需要的字节数,如果溢出,请将分配大小加倍。如有必要,请重复。

一次读取一个字节

这是极其低效的。您应该尝试读取当前缓冲区中尽可能多的可用字节。如果填满,请将缓冲区的大小加倍,然后继续读取(直到没有更多字节)。

不要使用与库函数相同的名称

你有一个名为

read

 的变量,它也是库函数的名称。不要那样做。我有点惊讶编译器没有抱怨。

您的读取循环可能应该类似于这样。我省略了错误检查,甚至省略了

poll()

 的内容。

int buff_size = 8192; char *buffp = malloc(buff_size); int bytes_read = 0; int slurp; while ((slurp = recv(fd, buffp + bytes_read, buff_size - bytes_read, 0)) > 0) { bytes_read += slurp; if (bytes_read == buff_size) { buff_size *= 2; buffp = realloc(buffp, buff_size); } }
    
© www.soinside.com 2019 - 2024. All rights reserved.