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.

Sunday, April 26, 2020

Simple Web Client Program

We are going to attempt a simple web client program.  We request a webpage from "yahoo.com".  Get the IP Address using ping command.  It is "98.137.246.7".  Use it in the program.

1
2
3
4
5
[root@svtap01end1 work]# ping yahoo.com
PING yahoo.com (98.137.246.7) 56(84) bytes of data.
64 bytes from media-router-fp1.prod1.media.vip.gq1.yahoo.com (98.137.246.7): icmp_seq=1 ttl=51 time=265 ms
64 bytes from media-router-fp1.prod1.media.vip.gq1.yahoo.com (98.137.246.7): icmp_seq=2 ttl=51 time=265 ms
64 bytes from media-router-fp1.prod1.media.vip.gq1.yahoo.com (98.137.246.7): icmp_seq=3 ttl=51 time=265 ms

 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
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>

int main(int argc, char const *argv[])
{
 int sock;

 // Create a socket
 if ((sock = socket(AF_INET,SOCK_STREAM,0))<0) {
  printf("socket creation error\n");
  return -1;
 }

 
 struct sockaddr_in serv_addr;
 serv_addr.sin_family=AF_INET;
 serv_addr.sin_port=htons(80);
 
 // Convert IP Address in binary format
 if(inet_pton(AF_INET,"98.137.246.7",&serv_addr.sin_addr) <= 0) {
  printf("Address conversion to binary\n");
  return -1;
 }

 // Connect client socket to the web server
 if(connect(sock,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0)
 {
  printf("Connection Error\n");
  return -1;
 }

 // Request for a web page by sending HTTP Get Packet
 if(write(sock, "GET / HTTP/1.1\r\n\r\n",strlen("GET / HTTP/1.1\r\n\r\n")) != strlen("GET / HTTP/1.1\r\n\r\n"))
 {
  printf("Error write\n");
  return -1;
 }

 char recvline[80];
 memset(recvline,0,80); //Fill all 80 characters to 0
 int i,readlen,linecounter=1;
 
 // Read the characters in batches of 80
 while ((readlen=read(sock,recvline,80)) >0)
 {
  printf("%d  -",linecounter);
  
  //Carriage Return and New Line Character has to be displayed as it is
  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");
 }

 //Close the socket
 close(sock);
 printf("Successful creation");
 return 0;
}

Regular steps involved in creating a socket:

Line 11 - Creating a socket.  Stream Socket as HTTP involves TCP.
Line 22 - IP Address to be copied in the binary format
Line 28 - Connect to yahoo server with the port 80

Retrieving Data will be:

Line 35 - Send a HTTP Get packet to retrieve a web page
Line 46 - Loop to retrieve characters in the batches of 80
Line 52 - Carriage Return and New Line Characters has to be displayed as \r and \n

Close the connection:

Line 65 - Close the socket

When executed, Line Batch number and the Contents are displayed as


# gcc -o webclient webclient.c
# ./webclient
1  -HTTP/1.1 400 Host Header Required\r\nDate: Sun, 26 Apr 2020 16:48:32 GMT\r\nConnecti
2  -on: close\r\nServer: ATS\r\nCache-Control: no-store\r\nContent-Type: text/html\r\nConten
3  -t-Language: en\r\nX-Frame-Options: SAMEORIGIN\r\nContent-Length: 4383\r\n\r\n<!DOCTYPE h
4  -tml>\n<html lang="en-us">\n  <head>\n    <meta http-equiv="content-type" content="t
5  -ext/html; charset=UTF-8">\n    <meta charset="utf-8">\n    <title>Yahoo</title>\n
6  -  <meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui">
7  -\n    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">\n    <style>\n
8  -      html {\n          height: 100%;\n      }\n      body {\n          background:
9  -#fafafc url(https://s.yimg.com/nn/img/sad-panda-201402200631.png) 50% 50%;\n
10  -     background-size: cover;\n          height: 100%;\n          text-align: cente
11  -r;\n          font: 300 18px "helvetica neue", helvetica, verdana, tahoma, arial,
12  - sans-serif;\n          margin: 0;\n      }\n      table {\n          height: 100%;\n
13  -          width: 100%;\n          table-layout: fixed;\n          border-collapse:
14  - collapse;\n          border-spacing: 0;\n          border: none;\n      }\n      h1
15  - {\n          font-size: 42px;\n          font-weight: 400;\n          color: #4000
16  -90;\n      }\n      p {\n          color: #1A1A1A;\n      }\n      #message-1 {\n
17  -     font-weight: bold;\n          margin: 0;\n      }\n      #message-2 {\n
18  -  display: inline-block;\n          *display: inline;\n          zoom: 1;\n
19  -  max-width: 17em;\n          _width: 17em;\n      }\n      </style>\n      <script>
20  -\n      \n      </script>\n  </head>\n  <body>\n  <!-- status code : 400 -->\n  <!-- H
21  -ost Header Required -->\n  <!-- host machine: media-router-fp1001.prod.media.gq1.
22  -yahoo.com -->\n  <!-- timestamp: 1587919712.936 -->\n  <!-- url: http:///-->\n  <sc
23  -ript type="text/javascript">\n    function buildUrl(url, parameters){\n      var q
24  -s = [];\n      for(var key in parameters) {\n        var value = parameters[key];\n
25  -        qs.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));\n
26  -  }\n      url = url + "?" + qs.join('&');\n      return url;\n    }\n\n    function
27  -generateBRBMarkup(site) {\n      params.source = 'brb';\n      generateBeaconMarku
28  -p(params);\n      var englishHeader = 'Will be right back...';\n      var englishM
29  -essage1 = 'Thank you for your patience.';\n      var englishMessage2 = 'Our engin
30  -eers are working quickly to resolve the issue.';\n      var defaultLogoStyle = ''
31  -;\n      var siteDataMap = {\n        'default': {\n          logo: 'https://s.yimg
32  -.com/rz/p/yahoo_frontpage_en-US_s_f_p_205x58_frontpage.png',\n          logoAlt:
33  -'Yahoo Logo',\n          logoStyle: defaultLogoStyle,\n          header: englishHe
34  -ader,\n          message1: englishMessage1,\n          message2: englishMessage2\n
35  -       }\n      };\n\n      var siteDetails = siteDataMap['default'];\n\n      docume
36  -nt.write('<table><tbody><tr><td>');\n      document.write('<div id="content">');\n
37  -      document.write('<img src="' + siteDetails['logo'] + '" alt="' + siteDetail
38  -s['logoAlt'] + '" style="' + siteDetails['logoStyle'] + '">');\n      document.wr
39  -ite('<h1 style="margin-top:20px;">' + siteDetails['header'] + '</h1>');\n      do
40  -cument.write('<p id="message-1">' + siteDetails['message1'] + '</p>');\n      doc
41  -ument.write('<p id="message-2">' + siteDetails['message2'] + '</p>');\n      docu
42  -ment.write('</div>');\n      document.write('</td></tr></tbody></table>');\n    }\n
43  -\n    function generateBeaconMarkup(params) {\n        document.write('<img src="'
44  - + buildUrl('//geo.yahoo.com/b', params) + '" style="display:none;" width="0px"
45  -height="0px"/>');\n        var beacon = new Image();\n        beacon.src = buildUr
46  -l('//bcn.fp.yahoo.com/p', params);\n    }\n\n    var hostname = window.location.hos
47  -tname;\n    var device = '-';\n    var ynet = ('-' === '1');\n    var time = new Da
48  -te().getTime();\n    var params = {\n        s: '1197757129',\n        t: time,\n
49  -     err_url: document.URL,\n        err: '400',\n        test: '-',\n        ats_h
50  -ost: 'media-router-fp1001.prod.media.gq1.yahoo.com',\n        rid: '-',\n        m
51  -essage: 'Host Header Required'\n    };\n\n    if(ynet) {\n        document.write('<d
52  -iv style="height: 5px; background-color: red;"></div>');\n    }\n    generateBRBMa
53  -rkup(hostname, params);\n\n  </script>\n  <noscript>\n  <table>\n    <tbody>\n      <t
54  -r>\n        <td>\n          <div id="englishContent">\n            <h1 style="margi
55  -n-top:20px;">Will be right back...</h1>\n            <p id="message-1">Thank you
56  -for your patience.</p>\n            <p id="message-2">Our engineers are working q
57  -uickly to resolve the issue.</p>\n          </div>\n        </td>\n      </tr>\n
58  -</tbody>\n  </table>\n  </noscript>\n  </body>\n</html>\n
Successful creation

Sunday, April 19, 2020

Threads in C - Input and Output

Today, we will see only to pass values to a thread.  Retrieve value from thread.

 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
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

//Thread to change value from 20 to 30
void * threadtak (void * poin)
{
    int *inttak = (int *)poin;
    *inttak=30;
    return NULL;
}

//Thread to return a value 60
void * threadret ()
{
  int *ret=malloc(sizeof(int));
  *ret=60;
  printf("%d\n",*ret);
  return ret;
}
int main()
{
    pthread_t tak,ret;
    pthread_attr_t attr;
    pthread_attr_init(&attr);

    int vartak=20;
    pthread_create(&tak, &attr,threadtak, &vartak);
    pthread_join(tak,NULL);
    printf("Modified vartak = %d\n",vartak);

    int *varret;
    pthread_create(&ret, &attr,threadret, NULL);
    pthread_join(ret,(void *)&varret);
    printf("Returned varret=%d\n",*varret);
    
    return 0;
}

Lets break the explanation into 2.

Passing the value

Line 29: Address of vartak is passed as argument
Line 9: As it is declared void pointer, it can take the address of integer/any other data type.
Line 10: Value at inttak is changed to 30
Line 11: Returned as NULL
Line 31: vartak is already changed and can be accessed as normal int


1
2
3
4
5
# gcc -pthread -o a.out main.c
# ./a.out
Modified vartak = 30
60
Returned varret=60

Value is vartak is displayed as 30

Returning the value

Line 33: We try to retrieve the value from threadret into integer pointer.
Line 34: As we are not passing any argument to the thread, we keep it NULL
Line 15: The argument for the thread can be kept blank as no argument is passed
Line 17: We create a memory and assign the value 60
Line 20: Return the address of ret
Line 35: Join should have void **.  We keep it &varret.  Because, *varret is value. varret is the address of *varret. &varret is the address of varret.


1
2
3
4
5
# gcc -pthread -o a.out main.c
# ./a.out
Modified vartak = 30
60
Returned varret=60

Value of varret is displayed as 60.

Sunday, April 12, 2020

Threads in C

We have seen how fork() works in C.  They say, threads are light-weight processes.  Will see how it is done.  Sample program with 3 threads is given here:

 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
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void * threadrun (void * poin)
{
    int k=*(int *)poin;
    
    for (int i=1;i<5;i++)
    {
      sleep(2);
      printf("iteration %d of thread %d\n",i,k);
    }
    return NULL;
}

int main()
{
    pthread_t tid1,tid2,tid3;
    pthread_attr_t attr;
    pthread_attr_init(&attr);

    int p=1;
    pthread_create(&tid1, &attr,threadrun, &p);
    int q=2;
    pthread_create(&tid2, &attr,threadrun, &q);
    int r=3;
    pthread_create(&tid3, &attr,threadrun, &r);
    
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    
    return 0;
}

The thread function should take in a void pointer, return a void pointer to a pointer.  The function threadrun will print 4 iterations each while sleeping 2 seconds for each iteration.  We are returning NULL.

Thread Creation
Line 19 - Create an identifier
Line 20/21 - Create an attribute structure and initialize
Line 23 - Create a value to pass it to thread, so that we can identify the thread
Line 24 - Start the Thread

After thread is created, they will start executing independent of each other.  You can see the stdout the sequence in which the prints are made.

Thread Join
Line 30 - We wait till thread1 is returned.
Line 31 - We wait till thread2 is returned
Line 32 - We wait till thread3 is returned

As Thread1 is first in line,  even though thread 2 and 3 returns earlier, our program will wait till thread1 is completed.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# ./thread
iteration 1 of thread 1
iteration 1 of thread 3
iteration 1 of thread 2
iteration 2 of thread 1
iteration 2 of thread 2
iteration 2 of thread 3
iteration 3 of thread 3
iteration 3 of thread 2
iteration 3 of thread 1
iteration 4 of thread 3
iteration 4 of thread 2
iteration 4 of thread 1