Dev Notes

Various Cheat Sheets and Resources by David Egan/Carawebs.

HTTP Server in C


C, HTTP, Sockets
David Egan

Simple http server socket in Linux. This is an educational work in progress, and these are my notes.

Server Socket

The basic procedure:

  • Create socket with socket() call
  • bind() this to an IP and port where it can
  • listen() for connections, then
  • accept() connection and send() or receive() data to/from connected sockets

Note that if struct sockaddr_in serverAddress.sin_addr.s_addr is set to INADDR_ANY the socket is bound to all local interfaces. INADDR_ANY is a constant set to zero, defined in netinet/in.h. This will correspond to an IP address of 0.0.0.0 in the standard IPv4 notation. Note that htonl(INADDR_LOOPBACK) and inet_addr("127.0.0.1") are functionally equivalent.

Simple Example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h> // for getnameinfo()

// Usual socket headers
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <arpa/inet.h>

#define SIZE 1024
#define BACKLOG 10  // Passed to listen()

void report(struct sockaddr_in *serverAddress);

void setHttpHeader(char httpHeader[])
{
    // File object to return
    FILE *htmlData = fopen("index.html", "r");

    char line[100];
    char responseData[8000];
    while (fgets(line, 100, htmlData) != 0) {
        strcat(responseData, line);
    }
    // char httpHeader[8000] = "HTTP/1.1 200 OK\r\n\n";
    strcat(httpHeader, responseData);
}

int main(void)
{
    char httpHeader[8000] = "HTTP/1.1 200 OK\r\n\n";

    // Socket code
    int serverSocket = socket(
        AF_INET,
        SOCK_STREAM,
        0
    );

    struct sockaddr_in serverAddress;
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(8001);
    serverAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);//inet_addr("127.0.0.1");
    // If `serverAddress.sin_addr.s_addr` is set to INADDR_ANY the socket is bound
    // to all local interfaces. INADDR_ANY is a constant set to zero, defined in
    // `netinet/in.h`. This will correspond to an IP address of 0.0.0.0 in the
    // standard IPv4 notation.
    // Note that `htonl(INADDR_LOOPBACK)` and `inet_addr("127.0.0.1")` are
    // functionally equivalent.

    bind(serverSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
    int listening = listen(serverSocket, BACKLOG);
    if (listening < 0) {
        printf("Error: The server is not listening.\n");
        return 1;
    }
    report(&serverAddress);
    int clientSocket;
    setHttpHeader(httpHeader);
    while(1) {
        clientSocket = accept(serverSocket, NULL, NULL);
        send(clientSocket, httpHeader, sizeof(httpHeader), 0);
        close(clientSocket);
    }
    return 0;
}

void report(struct sockaddr_in *serverAddress)
{
    char hostBuffer[INET6_ADDRSTRLEN];
    char serviceBuffer[NI_MAXSERV]; // defined in `<netdb.h>`
    socklen_t addr_len = sizeof(*serverAddress);
    int err = getnameinfo(
        (struct sockaddr *) serverAddress,
        addr_len,
        hostBuffer,
        sizeof(hostBuffer),
        serviceBuffer,
        sizeof(serviceBuffer),
        NI_NUMERICHOST
    );
    if (err != 0) {
        printf("It's not working!!\n");
    }
    printf("\n\n\tServer listening on http://%s:%s\n", hostBuffer, serviceBuffer);
}

Using getnameinfo

int getnameinfo(
    const struct sockaddr *sa,  // pointer to a generic socket address structure (of type sockaddr_in or sockaddr_in6)
    socklen_t salen,            // size of the above
    char *host,                 // Caller allocated buffer, will receive host
    socklen_t hostlen,          // Size of above
    char *serv,                 // Caller allocate buffer, will receive service name
    socklen_t servlen,          // Size of above
    int flags);

References


comments powered by Disqus