HTTP Push Client in Posix C

René Samselnig

Have you ever wanted to push a value to a HTTP page and don’t care about the result? Well, you can do it in pure C, but be prepared – it is not as trivial as you might think. Here is how to do it.

Includes

1
2
3
4
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
Distinguish Windows from Unix/Linux

We need to include winsock if we compile under Windows – otherwise use Unix/Linux headers for networking operations.

5
6
7
8
9
10
11
12
13
14
#ifdef _WIN32
#include <winsock.h>
#include <io.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define closesocket(s) close(s)
#endif

Defining the port to connect to

The default port to connect to is 80. You can change it here if you need to.

15
#define HTTP_PORT 80

The push_value Function

16
void push_value( char *host, char *path, char *value ) {
Variable Declarations
17
    time_t tt;

This is the place to save the actual date string to. It’s length is limited to 15 characters – 14 for the date string and one for the terminating \0 at the end.

18
    char datum[15];

The actual HTTP Request is being written to this variable.

19
    char request[300];

These are needed to create the socket/connection.

20
21
22
23
    struct sockaddr_in server;
    struct hostent *host_info;
    unsigned long addr;
    int sock;
Windows needs Winsock initialisation

Windows TCP initialisation has to be done only if compiled under Windows.

24
25
26
27
28
29
30
31
32
33
#ifdef _WIN32
    short wVersReq;
    WSADATA wsaData;
    wVersReq = MAKEWORD( 1, 1 );
    if( WSAStartup( wVersReq, &wsaData ) != 0 ) {
        fprintf( stderr,
                 "Failed to init windows sockets\n" );
        return;
    }
#endif
Get actual date

strftime creates the actual date into datum, it uses the format given (“%Y%m%d%H%M%S”) and the current time from time(NULL).

34
35
36
37
38
    tt = time(NULL);
    strftime( datum,
              sizeof( datum ),
              "%Y%m%d%H%M%S",
              localtime(&tt) );
Create a socket
39
40
41
42
43
        sock = socket( PF_INET, SOCK_STREAM, 0 );
        if( sock < 0 ) {
            perror( "failed to create socket" );
            return;
        }
Create struct for connection partner
44
45
    memset( &server, 0, sizeof( server ) );
    if( ( addr = inet_addr( host ) ) != INADDR_NONE ) {

host is a numerical IP Address. Nothing special has to be done.

46
47
48
        memcpy( (char *) &server.sin_addr,
                &addr, sizeof( addr ) );
    } else {

host is a domain name. Convert this domain name into a numerical IP Address.

49
50
51
52
53
54
55
56
57
58
59
        host_info = gethostbyname( host );
        if( NULL == host_info ) {
            fprintf( stderr,
                     "unknown server: %s\n",
                     host );
            return;
        }
        memcpy( (char *) &server.sin_addr,
                host_info->h_addr,
                host_info->h_length );
    }

Set server parameters.

60
61
    server.sin_family = AF_INET;
    server.sin_port = htons( HTTP_PORT );
Create connection with partner

Time to get together. Creating connection to server.

62
63
64
65
66
    if( connect( sock, (struct sockaddr*) &server,
                 sizeof( server ) ) < 0 ) {
        perror( "can't connect to server" );
        return;
    }

Create HTTP 1.0 request with given value value and current time datum.

67
68
69
70
71
    sprintf( request,
             "GET %s?value=%s&time=%s HTTP/1.0\n\n",
             path,
             value,
             datum);

Send created HTTP request to server. Ignore response, as it is not needed (see specification).

72
    send( sock, request, sizeof( request ), 0 );

Close socket, we don’t need it anymore – “fire and forget”.

73
74
    closesocket( sock );
}

Call push_value

This is a small main function that gets the host, path and value from the command line and calls the push_value function. Originally for testing purpose only, but I left it in for you to test it on your own.

75
76
77
78
79
80
81
82
83
int main( int argc, char **argv ) {
    if( argc < 3 ) {
        fprintf( stderr, "usage: %s host path value",
                 argv[0] );
        return -1;
    }
    push_value( argv[1], argv[2], argv[3] );
    return 0;
}

Conclusion

With a shell script it would have been a lot easier. Given the above C programme we could create an equivalent bash shell script to do that:

1
2
3
4
5
6
7
#!/bin/sh
if [ $# -lt 3 ]; then
    echo "usage: $0 host path value"
    exit -1
fi
TIME=$(date +%Y%m%d%H%M%S)
wget -q -O /dev/null "http://$1:80$2?value=$3&time=$TIME"

On the other hand this shell script takes about twice the time to complete than the C programme. Decide for yourself if it’s worth the cost.

If you like you can download the C Source Code or the Bash Script. Compile the C programme with gcc -o pushclient pushclient.c.



One Response to “HTTP Push Client in Posix C”

Leave a Reply