0

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.

Jack Greenhill
  • 10,240
  • 12
  • 38
  • 70
  • `recv()` will only return `0` when the peer shutdown the connection cleanly. Otherwise the return value is `-1`. It is better to check if `recv()` returned greater than `0`. – alvits Dec 27 '13 at 05:32
  • @alvits Just as expected, `recv()` returns the number of bytes read (normally < 512). However, when it is called again, it never returns. If I place a `printf()` before and after `recv()`, on the second iteration, the second `printf()` is never called. – Jack Greenhill Dec 27 '13 at 05:44
  • `recv()` will block if the sender has stopped sending. How is the sender sending? What flags does it use when calling `send()`? If the sender uses the flag MSG_MORE, then the kernel will not send until MSG_EOR a non MSG_MORE flag is received by the kernel or a MSG_EOR is received by the kernel, then the kernel will send the message to your server. When you receive less than the size you were expecting (512) then you already received the complete message. If the message you are expecting is more than the max (>512) then you will have to recv() more than once. – alvits Dec 27 '13 at 06:07
  • In other words, if you receive exactly 512, there may be more in the buffer thus the need to fetch more from the buffer using `recv()`. If you receive less than 512, then that's the end of it. – alvits Dec 27 '13 at 06:08

1 Answers1

0

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.

Armali
  • 18,255
  • 14
  • 57
  • 171