tpar44
tpar44

Reputation: 1451

Writing a webserver in C

I am trying to write a simple Webserver in C. As of now I can receive connections and receive messages in full. However, in accordance with the HTTP/1.0 protocol, I want to be able to send information back to the client when the "\r\n\r\n" sequence is encountered. However, when using Telnet to test my server, when I enter "\r\n\r\n", the server does nothing until I hit "^]" on the client. I tested this against Apache and Apache does not have this problem. So I was hoping to some information on how to mimic the Apache behavior. My code is added below but please keep in mind I am no where near done nor have I implemented a lot of error checking. Thanks!

main(){
        int sock_fd = 0;
        int client_fd = 0;
        struct sockaddr_in socket_struct;
        /*Creates the socket*/
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }/*Ends the socket creation*/

        /*Populates the socket address structure*/
        socket_struct.sin_family = AF_INET;
        socket_struct.sin_addr.s_addr=INADDR_ANY;
        socket_struct.sin_port =htons(port);

        if (bind(sock_fd, (struct sockaddr*) &socket_struct, sizeof(socket_struct)) < 0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the binding.

        if (listen(sock_fd, 5) <0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the listening function

        if ( (client_fd = accept(sock_fd, NULL, NULL)) <0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the accepting.
        while ( (size = read(client_fd, msg, 1024)) > 0)
        {
                //size = recv(client_fd, msg, 1024, MSG_PEEK|MSG_WAITALL);
                if ( (msg[size-4] == 13) && (msg[size-3] == 10)&&(msg[size-2] == 13) && (msg[size-1] == 10) )
                {
                        char* buffer = (char *)malloc(sizeof("The msg was: ")+ sizeof(msg));
                        sprintf(buffer, "The msg was: %s", msg);
                        send(client_fd, buffer, sizeof("The msg was: ")+ sizeof(msg), MSG_OOB);
                }

        }//ends the while loop for receiving data
        close(client_fd);
}

Upvotes: 3

Views: 2683

Answers (1)

Dietrich Epp
Dietrich Epp

Reputation: 213258

First of all, this line won't work:

while ( (size = read(client_fd, msg, 1024)) > 0)

This will receive chunks of messages, and every chunk you receive will overwrite the last chunk. Do something like this:

char msg[1024];
size_t pos = 0;
while (1) {
    assert(pos < sizeof(msg));
    ssize_t amt = read(client_fd, msg + pos, sizeof(msg) - pos);
    if (amt < 0)
        err(1, "read failed");
    if (amt == 0)
        break; // EOF
    pos += amt;
    char *e = strstr(msg, "\r\n\r\n");
    if (e) {
        size_t msglen = e - msg;
        /* Handle a complete message here */
    }
}

This way, as you receive chunks of the message, they get written into your buffer. Once you have the "\r\n\r\n" sequence, then you can deal with the whole message, even though you might get it in chunks.

Key lesson: In TCP, packet boundaries and message boundaries might be completely different, and the sizes you get from read might be different still. You have to find the end of a message by looking at the data itself, not by looking at how much data is returned from read() (except for EOF, which is signaled when read() returns 0).

Footnote: I don't think out-of-band data does what you think it does.

send(client_fd, buffer, sizeof("The msg was: ")+ sizeof(msg), MSG_OOB);

Out-of-band data is a quirky feature of TCP that should almost certainly be avoided in modern protocols. Its implementation differs from platform to platform in such a way that it is only safe to send one byte of out-of-band data.

It is used for some things by the Telnet protocol (and by extension, FTP, which is built on Telnet), but has no purpose in HTTP.

Upvotes: 2

Related Questions