Receiving data from socket using recv not working

2.3k views Asked by At

I'm trying to create a simple proxy server using BSD sockets, which listens on a port for a request and then passes that request on to another server, before sending the server's response back to the browser.

I am able to receive a REST request from the browser, using the code below:

void *buffer = malloc(512);
long length = 0;    

while (1) {
    void *tempBuffer = malloc(512);
    long response = recv(acceptedDescriptor, tempBuffer, 512, 0);

    if (response == 0 || response < 512) {
        free(tempBuffer);

        printf("Read %lu bytes\n", length);
        break;
    }

    memcpy(buffer + length, tempBuffer, response);
    free(tempBuffer);

    length += response;

    realloc(buffer, length + 512);
}

However, recv() should return 0 when the connection is closed by the peer (in this case the browser), but this is never the case. The only way I am able to detect whether or not the connection has closed is by checking if the response is less than the maximum amount requested from recv(), 512 bytes. This is sometimes problematic as some requests I see are incomplete.

If there is no more data to receive, recv() blocks and never returns, and setting the accepted descriptor to be non-blocking means that the read loop goes on forever, never exiting.

If I:

  1. Set the listening socket descriptor to non-blocking, I get a EAGAIN error (resource temporarily unavailable) when I try to accept() the connection

  2. Set the accepted socket descriptor to non-blocking, recv() never returns 0 and the loop continues on forever

  3. Set them both to non-blocking, I get a 'bad file descriptor' error when trying to accept() the connection

  4. Don't set either of them to non-blocking, the loop never exits because recv() never returns.

The socket itself is created as follows, but since it is able to detect a request, I can't see anything wrong with its initialisation:

int globalDescriptor = -1;
struct sockaddr_in localServerAddress;
...
int initSocket() {
    globalDescriptor = socket(AF_INET, SOCK_STREAM, 0);

    if (globalDescriptor < 0) {
        perror("Socket Creation Error");
        return 0;
    }

    localServerAddress.sin_family = AF_INET;
    localServerAddress.sin_addr.s_addr = INADDR_ANY;
    localServerAddress.sin_port = htons(8374);

    memset(localServerAddress.sin_zero, 0, 8);

    int res = 0;
    setsockopt(globalDescriptor, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));

    //fcntl(globalDescriptor, F_SETFL, O_NONBLOCK);

    return 1;
}
...
void startListening() {
    int bindResult = bind(globalDescriptor, (struct sockaddr *)&localServerAddress, sizeof(localServerAddress));

    if (bindResult < 0) {
        close(globalDescriptor);
        globalDescriptor = 0;

        perror("Socket Bind Error");
        exit(1);
    }

    listen(globalDescriptor, 1);

    struct sockaddr_in clientAddress;
    int clientAddressLength = sizeof(clientAddress);

    while (1) {
        memset(&clientAddress, 0, sizeof(clientAddress));
        clientAddressLength = sizeof(clientAddress);

        int acceptedDescriptor = accept(globalDescriptor, (struct sockaddr *)&clientAddress, (socklen_t *)&clientAddressLength);
        //fcntl(acceptedDescriptor, F_SETFL, O_NONBLOCK);

        if (acceptedDescriptor < 0) {
            perror("Incoming Connection Error");
            exit(1);
        }

        void *buffer = malloc(512);
        long length = 0;

        while (1) {
            void *tempBuffer = malloc(512);
            long response = recv(acceptedDescriptor, tempBuffer, 512, 0);

            if (response == 0) {
                free(tempBuffer);

                printf("Read %lu bytes\n", length);
                break;
            }

            memcpy(buffer + length, tempBuffer, response);
            free(tempBuffer);

            length += response;

            realloc(buffer, length + 512);
        }

        executeRequest(buffer, length, acceptedDescriptor);

        close(acceptedDescriptor);
        free(buffer);
    }
}
...

The startListening() function is called only if initSocket() returns 1:

int main(int argc, const char *argv[]) {
    if (initSocket() == 1) {
        startListening();
    }

    return 0;
}

I'm probably doing something stupid here, but I'd appreciate any information you may have about this problem and how I could fix it.

1

There are 1 answers

0
Armali On

Since your REST request is a HTTP method, it has the well-defined HTTP Message Length, so you just have to recv() until the complete message has arrived.