我设置了一个基本的套接字连接,我已经为服务器编写了代码,我暂时只是使用 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);
由于您没有提供可重现的示例,我不能说这个答案包含修复程序,但我看到了这些问题:
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);
}
}