I have a basic socket connection set up, where I have written code for a server. I am, temporarily, just using Google Chrome with localhost to test out if my code can receive messages properly.
I had some issues with recv() blocking, so I used poll(), as I saw suggested online, and after doing a quick search on the differences between select() and poll().
My code is able to retrieve the message, but poll() still thinks that there is a message to read even though there is apparently not (because recv() is blocking).
Here is my simplified code:
/*
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);
}
Running this, I get a final output (the last print statement that is run's output) of the following:
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
After this, the program stalls, and by checking the debugger I see that it is the recv() (in the sample code above, but shown below again for convenience) that is blocking the flow.
// ... is indicating that there is stuff before/after it
...if (recv(*connectionptr, data_ptr, 1, 0) < 0)...
The socket is configured with keep alive (I tried disabling this, because I thought it might be the problem, but it didn't work) and address reuse.
Edit:
Following is the full code up to the problem (code after the problem has been excluded), as I was told that this isn't enough to fulfill the need to be reproducible:
#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;
}
server.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);
Since you haven't provided a reproducible example, I can't say that this answer contains a fix, but I see these problems:
realloc()can return a different pointerYour code calls
realloc()like so:However, you're also maintaining another variable,
data_ptr, which points into your buffer.realloc()can return a different pointer than was passed to it. So you'll need to abandon yourdata_ptridea. Instead, track the number of bytes you have read so far, and use that as an index into your current buffer pointer (which can change via therealloc()).Dereferencing the
data_ptrpointer ifrealloc()gave you a different pointer will invoke undefined behaviour. This could well be the source of your problems.Increasing your allocation by one byte at a time
This is incredibly inefficient. You should allocate as many bytes as you think you might need in a typical case, and if you overflow it, double the size of the allocation. Repeat if necessary.
Reading one byte at a time
This is incredibly inefficient. You should try to read as many bytes as are free in your current buffer. If you fill it, double the size of the buffer, and keep reading (until you get no more bytes).
Don't use the same name as library functions
You have a variable called
read, which is also the name of a library function. Don't do that. I'm somewhat surprised the compiler didn't complain.Your read loop should probably resemble something like this. I've omitted error checking and even the
poll()stuff.