Writing a simple Web Server in C

First we need to see how web server works.
1. Web browser(Client) requests a resource
2. Web Server serves the requested resource.

First lets write the simplest web server. It returns hello world for every requests web browser makes.
When web server starts,
1. It should create a socket - socket()
2. Then it should bind the socket to a address - bind()
3. Then listen to incoming connections - listen()
4. Accept connections - accept()

source code
#include<netinet/in.h>    
#include<stdio.h>    
#include<stdlib.h>    
#include<sys/socket.h>    
#include<sys/stat.h>    
#include<sys/types.h>    
#include<unistd.h>    
    
int main() {    
   int create_socket, new_socket;    
   socklen_t addrlen;    
   int bufsize = 1024;    
   char *buffer = malloc(bufsize);    
   struct sockaddr_in address;    
 
   if ((create_socket = socket(AF_INET, SOCK_STREAM, 0)) > 0){    
      printf("The socket was created\n");
   }
    
   address.sin_family = AF_INET;    
   address.sin_addr.s_addr = INADDR_ANY;    
   address.sin_port = htons(15000);    
    
   if (bind(create_socket, (struct sockaddr *) &address, sizeof(address)) == 0){    
      printf("Binding Socket\n");
   }
    
    
   while (1) {    
      if (listen(create_socket, 10) < 0) {    
         perror("server: listen");    
         exit(1);    
      }    
    
      if ((new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen)) < 0) {    
         perror("server: accept");    
         exit(1);    
      }    
    
      if (new_socket > 0){    
         printf("The Client is connected...\n");
      }
        
      recv(new_socket, buffer, bufsize, 0);    
      printf("%s\n", buffer);    
      write(new_socket, "hello world\n", 12);    
      close(new_socket);    
   }    
   close(create_socket);    
   return 0;    
}

Now lets add more to the previous code. Normally web servers send headers (just like the request from browsers) with status codes. so lets replace 46th line with few more lines.
write(new_socket, "HTTP/1.1 200 OK\n", 16);
write(new_socket, "Content-length: 46\n", 19);
write(new_socket, "Content-Type: text/html\n\n", 25);
write(new_socket, "<html><body><H1>Hello world</H1></body></html>",46);

Now you can extend this with other functions such as,
  1. Serving files (you can use sendfile() function)
  2. Serving different content types(images, zip, pdf etc)
  3. Different processes to handle requests (fork())
  4. Other http status codes
  5. Processing PHP (process PHP with system commands and write the result to the socket)

12 comments:

  1. Hi, man!

    Two simple mistakes:

    - You need to use new_socket in the "write()" HTML output
    - You forgot to include a "\n" in the "Content-length" line

    Your idea about processing PHP is both original and very good: I'm planning to use it in a actual classroom (I'm a teacher in Brazil).

    Best regards!

    ReplyDelete
  2. Nice code, however length is wrong in Content-Type line

    write(new_socket, "Content-Type: text/html\n\n", 27);

    should be

    write(new_socket, "Content-Type: text/html\n\n", 25);

    ReplyDelete
    Replies
    1. Thanks for the comment. u'r correct. I've counted "\n" as two characters. (I used character counter to count characters).

      Delete
  3. Hi Manula,

    Great post !
    I am using your code; but it is not giving any response to browser( even after adding HTML part as you described).
    This is the server code I am using :

    #include
    #include
    #include
    #include
    #include
    #include

    int init()
    {
    WSADATA wsaData;
    int iResult;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
    printf("WSAStartup failed: %d\n", iResult);
    return 1;
    }

    }

    void clean()
    {
    WSACleanup();
    }

    int main() {
    init();
    int create_socket, new_socket;
    int addrlen;
    int bufsize = 1024;
    char *buffer = malloc(bufsize);
    struct sockaddr_in address;

    if ((create_socket = socket(AF_INET, SOCK_STREAM, 0)) > 0){
    printf("The socket was created\n");
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    if (bind(create_socket, (struct sockaddr *) &address, sizeof(address)) == 0){
    printf("Binding Socket\n");
    }


    while (1) {
    if (listen(create_socket, 10) < 0) {
    perror("server: listen");
    exit(1);
    }

    if ((new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen)) < 0) {
    perror("server: accept");
    exit(1);
    }

    if (new_socket > 0){
    printf("The Client is connected...\n");
    }

    recv(new_socket, buffer, bufsize, 0);
    printf("%s\n", buffer);
    write(new_socket, "HTTP/1.1 200 OK\n", 16);
    write(new_socket, "Content-length: 46\n", 17);
    write(new_socket, "Content-Type: text/html\n\n", 25);
    write(new_socket, "HTML_DATA_HERE_AS_YOU_MENTIONED_ABOVE",46);
    close(new_socket);
    }
    close(create_socket);
    return 0;
    }



    I followed all your steps but still I am not getting any response on web browser.

    Please can you have a look into this ?

    Thanks !
    Kartik

    ReplyDelete
    Replies
    1. Hi,
      I tested your code in linux(without init and cleanup). And it works fine. But you have to change the following parts

      write(new_socket, "HTTP/1.1 200 OK\n", 16);
      write(new_socket, "Content-length: 370\n", 18);
      write(new_socket, "Content-Type: text/html\n\n", 25);
      write(new_socket, "HTML_DATA_HERE_AS_YOU_MENTIONED_ABOVE\n",38);

      You always have to change the "Content-length:" value accordingly. And those values in the end (16, 18,25,38) should also changed.

      When you are debugging first use a tool like telnet to check you cant connect to the port. For example telnet localhost 8080

      Delete
  4. I forgot to mention that I am running on Windows XP thus used init() and cleanup() methods.

    ReplyDelete
  5. HI im a newbie in this, im just wondering how to use a web browser to connect to the server ?

    ReplyDelete
    Replies
    1. Type the IP address and port in the web browsers address bar. If the web server is running on your machine type http://localhost:15000

      Delete
  6. Can someone help me to re-implement this to have a browser as a client, that request for a certain kind of file (img, video, sound) using http ?

    ReplyDelete
    Replies
    1. Hi, this works with a web browser. If you want to send a video or sound file you can read the file and write to the socket http://stackoverflow.com/questions/2014033/send-and-receive-a-file-in-socket-programming-in-linux-with-c-c-gcc-g. And you have to change the content type (MIME type) accordingly http://webdesign.about.com/od/multimedia/a/mime-types-by-content-type.htm

      Delete
  7. Hi Manula! I have a question if i would that when i write: 127.0.0.1:1500/luigi the server doesn't reply with hello world but with luigi? The question is that after "/" the text would be random. I would that my code reply (at monitor) with the same text (after /). i hope to be clear

    ReplyDelete