Monday, April 27, 2020

Simple web server program

Today we discuss simple web server program.  It accepts only one connection from a web client.  Reads the HTTP messages from the client.  Sends the HTTP message to the client.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
 int servsockfd,clisockfd;
 struct sockaddr_in servaddr, cliaddr;
 
 // Create a TCP Socket
 servsockfd=socket(AF_INET,SOCK_STREAM,0);
 if (servsockfd == -1)
 {
  printf("socket creation failed\n");
  exit(0);
 }
 
 //Populate IP Address and Port Number
 servaddr.sin_family=AF_INET;
 servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
 servaddr.sin_port=htons(80);
 
 //Bind the socket to the Structure sockaddr_in containing IP address and Port number 
 if((bind(servsockfd,(struct sockaddr *)&servaddr, sizeof(servaddr))) != 0)
 {
  printf("socket bind failed with %d\n",errno);
  exit(0);
 }
 
 //Start the server and keep listening for connections
 if((listen(servsockfd,5)) != 0)
 {
  printf("Listen failed\n");
  exit(0);
 } else
  printf("Waiting for client to connect...\n");
 
 //Accept the client connection
 int cliaddr_len=sizeof(cliaddr);
 clisockfd = accept(servsockfd,(struct sockaddr *)&cliaddr, &cliaddr_len);
 
 if(clisockfd <0)
 {
  printf("server accept failed\n");
  exit(0);
 } else
  printf("Server accepted client\n");

 //Store and display the client IP address and Port details
 char *client_ipaddr = inet_ntoa(cliaddr.sin_addr);
 int client_port=ntohs(cliaddr.sin_port);
 char client_port_ascii[10];
 sprintf(client_port_ascii,"%d",client_port);
 printf("Client IP = %s\nClient Port Number=%d\n",client_ipaddr,client_port);
  
 char recvline[60];
 int i,readlen,linecounter=1;
 
 // Read the characters in batches of 60
 while ((readlen=read(clisockfd,recvline,60)) >0)
 {
  printf("%d  -",linecounter,recvline);
  
  //Print the HTTP messages from the client in batches of 60 characters
  for (i=0; i<readlen; i++) { 
   if (recvline[i] == '\n') {
    printf("\\n");
   } else if (recvline[i] == '\r') {
    printf("\\r");
   } else {
    printf("%c",recvline[i]);
   }
  }
  linecounter++;
  printf("\n");
  
  //Break the loop if it sees \r\n at the end of messagae
  if(recvline[readlen-1] == '\n' && recvline[readlen-2] == '\r')
  {
   break;
  }
 }
 
 //Make a HTTP Message to send to client
 char msg[80];
 strcat(msg,"HTTP/1.1 200 OK\nContent-Type: text/plain\n\nHello world!\n");
 strcat(msg,"Your IP Address:");
 strcat(msg,client_ipaddr);
 strcat(msg,"\nYour Port Number:");
 strcat(msg,client_port_ascii);
 
 //Send the message to client
 write(clisockfd,msg,strlen(msg));
 
 //Close the connections
 close(clisockfd);
 close(servsockfd);
}

Listening to the HTTP Port

Line 13 - Create a TCP Socket Stream
Line 23 - Fill in the details of IP Address on which server has to run and also port number
Line 27 - Bind the socket and the structure (Observe that 2nd argument is typecasted as 'struct sockaddr  *' for the pointer of 'struct sockaddr_in')
Line 34 - Start listening for connections

Accepting the client

Line 43 - Accept the connection. (Observe that 2nd argument is type casted as 'struct sockaddr  *' for the pointer of 'struct sockaddr_in'.  Also the 3rd argument is pointer to the integer of size, not integer)
Line 43 - Return value of Accept function is client socket ID.  This is the ID that we use for reading and writing from that particular client.
Line 53 - Extract IP Address and Port number from the struct sockaddr cliaddr.

Structure of sockaddr_in will be like

#include <netinet/in.h>

struct sockaddr_in {
    short            sin_family;   // e.g. AF_INET
    unsigned short   sin_port;     // e.g. htons(3490)
    struct in_addr   sin_addr;     // see struct in_addr, below
    char             sin_zero[8];  // zero this if you want to
};

struct in_addr {
    unsigned long s_addr;  // load with inet_aton()
};

Read Client Message

When a browser is opened with the IP Address of machine, it sends out HTTP Get message.
Line 64 - While loop that displays the incoming message from client in batches of 60 characters
And breaking the loop whenever it seen \r\n at the end of message.  We are hoping that \r and \n doesnt come in different batches.  The probability of occurrence is less

Write Client Message

Line 89 - Start composing Hello Message along with its IP Address and Client Port number
Line 97 - 'Write' the message.

Close Sockets

Line 99 - Close the client socket and also server socket.

Compiling and Executing

# gcc -o webserver webserver.c
# ./webserver
Waiting for client to connect...
Client IP = 10.230.45.13
Client Port Number=63836
Server accepted client
1  -GET / HTTP/1.1\r\nHost: 10.123.8.110\r\nConnection: keep-alive\r\n

This is the execution of the program.  Means at the server end.  We use curl command line web client, for simplicity.  So, the GET message is very simple.

Web Client View

Use the curl command and the IP address.  We can see the Hello Message, IP Address and Port Numbers as well.


# curl http://10.123.8.110/
Hello world!
Your IP Address:10.123.8.231
Your Port Number:50416

Instead of curl, we can issue http://10.123.8.110 from chrome or firefox.  The messages will be seen on browser.

No comments:

Post a Comment