C - HTTP Socket Programming

Hello everybody,

I learning socket programming in C and was wondering if anybody here could help me out.

I have two .c programs namely server.c and client.c .......

The client sends a message and the server in turn returns the value.

Basically I want to send in a request to a webserver say at port 80 in form of

and in return get value dumped back.

Here are the codes for Server.c and Client.c

Server.c

#include <stdio.h>      /* for printf() and fprintf() */ 
#include <sys/socket.h> /* for socket(), bind(), and connect() */ 
#include <arpa/inet.h>  /* for sockaddr_in and inet_ntoa() */ 
#include <stdlib.h>     /* for atoi() and exit() */ 
#include <string.h>     /* for memset() */ 
#include <unistd.h>     /* for close() */ 
 
#define RCVBUFSIZE 64   /* Size of receive buffer */ 
#define MAXPENDING 5    /* Maximum outstanding connection requests */ 
 
void DieWithError(char *errorMessage);  /* Error handling function */ 
void HandleTCPClient(int clntSocket);   /* TCP client handling function */ 
 
int main(int argc, char *argv[]) 
{ 
    int servSock;                    /* Socket descriptor for server */ 
    int clntSock;                    /* Socket descriptor for client */ 
    struct sockaddr_in echoServAddr; /* Local address */ 
    struct sockaddr_in echoClntAddr; /* Client address */ 
    unsigned short echoServPort;     /* Server port */ 
    unsigned int clntLen;            /* Length of client address data structure */ 
 
    if (argc != 2)     /* Test for correct number of arguments */ 
    { 
        fprintf(stderr, "Usage:  %s <Server Port>\n", argv[0]); 
        exit(1); 
    } 
 
    echoServPort = atoi(argv[1]);  /* First arg:  local port */ 
 
    /* Create socket for incoming connections */ 
    if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 
        DieWithError("socket() failed"); 
       
    /* Construct local address structure */ 
    memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */ 
    echoServAddr.sin_family = AF_INET;                /* Internet address family */ 
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ 
    echoServAddr.sin_port = htons(echoServPort);      /* Local port */ 
 
    /* Bind to the local address */ 
    if (bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) 
        DieWithError("bind() failed"); 
 
    /* Mark the socket so it will listen for incoming connections */ 
    if (listen(servSock, MAXPENDING) < 0) 
        DieWithError("listen() failed"); 
 
    for (;;) /* Run forever */ 
    { 
        /* Set the size of the in-out parameter */ 
        clntLen = sizeof(echoClntAddr); 
 
        /* Wait for a client to connect */ 
        if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr,  
                               &clntLen)) < 0) 
            DieWithError("accept() failed"); 
 
        /* clntSock is connected to a client! */ 
 
        printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr)); 
 
        HandleTCPClient(clntSock); 
    } 
    /* NOT REACHED */ 
}//-- end main --// 
 
 
 
// 
//// 
// 
void DieWithError(char *errorMessage) 
{ 
    perror(errorMessage); 
    exit(1); 
} 
 
 
 
// 
//// 
// 
void HandleTCPClient(int clntSocket) 
{ 
    char echoBuffer[RCVBUFSIZE];        /* Buffer for echo string */ 
    int recvMsgSize;                    /* Size of received message */ 
 
    /* Receive message from client */ 
    if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) 
        DieWithError("recv() failed"); 
 
    /* got incoming request from client in the echobuffer */ 
    recvMsgSize = sprintf(echoBuffer, 
                          "MOTOR=0,TEMPS are 3A,42,FA,99");//reply!  
 
    /* Send received string and receive again until end of transmission */ 
    while (recvMsgSize > 0)      /* zero indicates end of transmission */ 
    { 
        /* Echo message back to client */ 
        if (send(clntSocket, echoBuffer, recvMsgSize, 0) != recvMsgSize) 
            DieWithError("send() failed"); 
 
        /* See if there is more data to receive */ 
        if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) 
            DieWithError("recv() failed"); 
    } 
 
    close(clntSocket);    /* Close client socket */ 
} 

Client.c

#include <stdio.h>      /* for printf() and fprintf() */ 
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */ 
#include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */ 
#include <stdlib.h>     /* for atoi() and exit() */ 
#include <string.h>     /* for memset() */ 
#include <unistd.h>     /* for close() */ 
 
#define RCVBUFSIZE 64   /* Size of receive buffer */ 
 
void DieWithError(char *errorMessage);  /* Error handling function */ 
 
int main(int argc, char *argv[]) 
{ 
    int sock;                        /* Socket descriptor */ 
    struct sockaddr_in echoServAddr; /* Echo server address */ 
    unsigned short echoServPort;     /* Echo server port */ 
    char *servIP;                    /* Server IP address (dotted quad) */ 
    char *echoString;                /* String to send to echo server */ 
    char echoBuffer[RCVBUFSIZE];     /* Buffer for echo string */ 
    unsigned int echoStringLen;      /* Length of string to echo */ 
    int bytesRcvd, totalBytesRcvd;   /* Bytes read in single recv()  
                                        and total bytes read */ 
 
    if ((argc < 3) || (argc > 4))    /* Test for correct number of arguments */ 
    { 
       fprintf(stderr, "Usage: %s <Server IP> <Echo Word> [<Echo Port>]\n", 
               argv[0]); 
       exit(1); 
    } 
 
    servIP = argv[1];             /* First arg: server IP address (dotted quad) */ 
    echoString = argv[2];         /* Second arg: string to echo */ 
 
    if (argc == 4) 
        echoServPort = atoi(argv[3]); /* Use given port, if any */ 
    else 
        echoServPort = 7;  /* 7 is the well-known port for the echo service */ 
 
    /* Create a reliable, stream socket using TCP */ 
    if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 
        DieWithError("socket() failed"); 
 
    /* Construct the server address structure */ 
    memset(&echoServAddr, 0, sizeof(echoServAddr));     /* Zero out structure */ 
    echoServAddr.sin_family      = AF_INET;             /* Internet address family */ 
    echoServAddr.sin_addr.s_addr = inet_addr(servIP);   /* Server IP address */ 
    echoServAddr.sin_port        = htons(echoServPort); /* Server port */ 
 
    /* Establish the connection to the echo server */ 
    if (connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) 
        DieWithError("connect() failed"); 
 
    echoStringLen = strlen(echoString);          /* Determine input length */ 
 
    /* Send the string to the server */ 
    if (send(sock, echoString, echoStringLen, 0) != echoStringLen) 
        DieWithError("send() sent a different number of bytes than expected"); 
 
    /* Receive the same string back from the server */ 
    totalBytesRcvd = 0; 
    printf("Received: ");                /* Setup to print the echoed string */ 
    while (totalBytesRcvd < echoStringLen) 
    { 
        /* Receive up to the buffer size (minus 1 to leave space for 
           a null terminator) bytes from the sender */ 
        if ((bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0)) <= 0) 
            DieWithError("recv() failed or connection closed prematurely"); 
        totalBytesRcvd += bytesRcvd;   /* Keep tally of total bytes */ 
        echoBuffer[bytesRcvd] = '\0';  /* Terminate the string! */ 
        printf("%s", echoBuffer);      /* Print the echo buffer */ 
    } 
 
    printf("\n");    /* Print a final linefeed */ 
 
    close(sock); 
    exit(0); 
}//-- end main --// 
 
 
 
// 
//// 
// 
void DieWithError(char *errorMessage) 
{ 
    perror(errorMessage); 
    exit(1); 
} 

Please advice as how I could achieve this.:o
Any sort of example will be great!

Thank you for using code tags. :slight_smile:

It means changing the logic on the client code a little. For starters, being able to get things by hostname instead of IP is nice:

#include <netdb.h>
...
    echoServAddr.sin_family      = AF_INET;             /* Internet address family */
    {
      struct in_addr a;
      struct hostent *h=gethostbyname(servIP);
      if(h == NULL)
        DieWithError("Unknown host");

      memcpy(&a, h->h_addr_list[0], sizeof(a));

      servIP=inet_ntoa(a);
    }
    echoServAddr.sin_addr.s_addr = inet_addr(servIP);   /* Server IP address */
...

After that I can already get limited output from web pages:

$ ./client mywebsite 'GET /
' 80
Received: 
<!-- Beginning of page_begin() output -->
<html><head>
  <titl
$

Notice the newline at the end of 'GET /', that is necessary.

Then you modify the receiving loop to receive as much data as it can until the loop breaks.

Note that there are a lot of HTTP standards that this does not comply to. for one thing, it doesn't tell the server it which website it wants to connect to, so it could easily be given the wrong page, and for another thing, it won't correctly parse multi-part web pages. Unless you really, absolutely need to make a new tool for this, the wget commandline tool is a full-featured standards-compliant way to automatically retrieve pages from the web.

Thanks 'Corona68' for the help:b:
I will tryout your code and get back to you.

Thanks again for your help.....

The example you gave me get the data alright......but how could I modify it further
so that I could put in a request on the client side on certain port

and then on the server side get a message saying

Thanks in Advance

There's plenty of examples of printf and argument handling in the code. What precisely is the sticking point?

Right! Well main thing I want is to GET the 'index.html' of a website at PORT 80 and then have a message up on the server.c saying "handling request"...etc

Thanks

Yes, I understand that's what you want to do, I'm wondering what's the sticking point in doing so. Are you having trouble figuring out where in the program logic these things need to be put? Are you trying to figure out how to accept a parameter on the command line? Are the finer points of printf a problem? Or am I off the mark and you need some other information? As is you're asking me to do everything and the idea is to teach...

[quote]

Oh no I understand clearly what you mean by teaching/learning.....:b:

The code you gave me earlier is exactly what I am after.......but I would like to extract all the information in the index.html..etc file in html format.

The purely reason for this is to understand how C sockets is able to extract
the data:o

They work much the same as C files, you just read from them. You read data from them with the recv() system call instead of the read() one but it's the same idea. You can see its manual page via 'man recv'.

Okay:b:

Also in the code below which u posted earlier y does it only extracting the first three lines on the html part? How can it extract the whole content of the html file?

Thanks

$ ./client mywebsite 'GET /
' 80
Received: 
<!-- Beginning of page_begin() output -->
<html><head>
  <titl
$

recv() is not guaranteed to get all the data at once; the data stream might not have been completely sent yet, or didn't all fit in the buffer you gave it, and so on. Just call recv() again to get more data.

You were right about the buffer......I increased the size from 64 to 500:o

It is getting interesting:D

So anyways, say if a host has a file called test.html! Could I simply use fprintf(), fopen() etc functions????

Also please guide me as to how I could apply it to my code?

If you've only received 64 bytes out of the 5 megabytes in transit, a larger buffer size won't help you. Streams can't tell how much is left to be received. So you still need multiple reads unless you know for a fact exactly how much the other end is sending and when.

To do what? You can't open a socket with fopen(), if that's what you mean.

Ok SIR!:o

Well I am not really fussed about how much I get back host as long as I get back some info.:b:

-----------------------------------------------------------------------

At the moment I can connect to a server as a client.

but how could I acheive the following:

  1. Client sends a request to the Server for "GET /index.html HTTP/1.1\r\n\r\n".
  2. Server accepts the connection and then sends the requested material back to the client basically dumping the information of index.htmlo on the client.

I am not asking you to post a solution but just for your professional guidance!

Thanks

Could you rephrase that? I'm not sure what you were trying to say.

Oh, you want to make a web server? The client needs to send() this information over the socket, see man 2 send.

This leaves out a whole bunch of steps, which may be why you're stymied.

  • Accept the connection
  • Read from it until \r\n\r\n -- recv() cares not about linefeeds, it just gives you chunks of data, so you may need to do a loop reading one character at a time in order to spot when the line ends.
  • Process the request -- you need to break "GET /index.html HTTP/1.1" into its component parts. You may be able to do this with sscanf():
    text char code[64], url[512], standard[64]; sscanf(buf, "%s %s %s", code, url, standard);
    This is a fairly quick-and-dirty solution. A better way may be to use strtok().
  • Convert /index.html into /path/to/webroot/index.html -- standard C string functions should do here.
    text char filename[512]; FILE *fp; strcpy(filename, "/path/to/webroot/"); strcat(filename, webfile);
  • Open the given file, read from it, and send it to the socket
    text size_t sent; char buf[512]; FILE *fp=fopen(filename, "r"); if(fp == NULL) { fprintf(stderr, "Couldn't open %s\n", filename); return(1); } while( (sent=fread(buf, 512, 1 fp)) > 0) { send(sock, buf, sent, 0); } fclose(fp);

Thanks again for your explanation I appreciate it:b:

But I have sort of understood what you are trying to say....:o

First of all this code which you posted in your first post, would it be possible for you to explain me what its doing please?

echoServAddr.sin_family      = AF_INET;             /* Internet address family */ 
    {
      struct in_addr a;
      struct hostent *h=gethostbyname(servIP);
      if(h == NULL)
        DieWithError("Unknown host");

      memcpy(&a, h->h_addr_list[0], sizeof(a));
 
      servIP=inet_ntoa(a);
    }    
    echoServAddr.sin_addr.s_addr = inet_addr(servIP);   /* Server IP address */

Also just for beginning how can I send a simple GET HTTP request and get back the result........so that I can get an idea of how to implement it further....

You have? Or do you mean you haven't? And I still don't understand what I asked about last time. I regret to say it, but I think the language barrier is making this extremely difficult...

It uses gethostbyname() to convert the address given into something you can use to make sockets. See man gethostbyname and man inet_ntoa(a).

See my last post to you for step-by-step details on this.

Sorry Dude! About the language barrier.......:rolleyes:Don't worry I am a English literate.....;):smiley:

Anyhow back to the topic yes I haven't understood what you posted earlier......please care to explain again......

As I am not sure where's what going into client and server.....may be I need to explore more about socket programming which I am researching.....:b:

Thanks for explaining this.

I shall look into it more.....

I'll explain in more detail how sockets are created and used.

There are lots and lots of kinds of sockets in UNIX, which is why the socket() call is so complex, but we're only interested in TCP sockets so we can disregard a lot of that.

  • Server
    [list]
  • Create the socket:

    text servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    It doesn't have any address or port at this point. It just exists, kind of blank.
  • Describe the address and port we want the socket to have, and bind it to it:
    text /* Construct local address structure */ memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */ echoServAddr.sin_family = AF_INET; /* Internet address family */ echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ echoServAddr.sin_port = htons(echoServPort); /* Local port */ /* Bind to the local address */ if (bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("bind() failed");
    This is also more complex than it needs to be since the bind() call can describe many different kinds of addresses. For TCP/IP, the family will always be AF_INET. INADDR_ANY is just the IP address 0.0.0.0; giving it this address tells the socket to respond on any IP addresses your machine offers. If we gave it the IP address of one particular interface, it would only respond on that interface. If we gave it an IP address the machine didn't have, it'd just fail to bind the socket. htonl() makes sure the bytes of the address are in the correct order. htons() does the same for the port.
  • Accept a connection on the socket:

    text /* Set the size of the in-out parameter */ clntLen = sizeof(echoClntAddr); /* Wait for a client to connect */ if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) DieWithError("accept() failed"); /* clntSock is connected to a client! */ printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));
    The accept() call will wait until someone connects to the socket. When someone does, it gives you a new, seperate file handle you can send() and recv() with, and fills out a structure explaining where the connection came from, in this case, echoCIntAddr.
    [/list]
  • Client
    [list]
  • The socket is created the same way.
  • But instead of bind(), we use connect():

    text /* Construct the server address structure */ memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */ echoServAddr.sin_family = AF_INET; /* Internet address family */ echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* Server IP address */ echoServAddr.sin_port = htons(echoServPort); /* Server port */ /* Establish the connection to the echo server */ if (connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("connect() failed");

    Note how instead of INADDR_ANY like the server, we give it the server's actual IP address to connect to.

    connect() doesn't create a new socket like accept() does, it converts the existing socket into one you can send() and recv() with.
    [/list]

Once again thank you for your prolonged explanation:rolleyes:.

                 [

](http://www.unix.com/high-level-programming/106672-c-http-socket-programming-post302307755.html\#post302307755)

[LEFT]Well I think this has become more of a English lesson than a learning exercise in 'socket-programming', as there was no need to point out how good or bad my English is or was. Also I have a habit of putting (.........) between comments when posting on a forum.

Perhaps I shall be more formal so that people are able to understand what I am typing :rolleyes:[/LEFT]