Data serialization

I recently needed a reliable way to pass binary information between two processes through an IPC mechanism. One of the programs (P1) consumes data from a DB2 database which is kinda complex to work with when the code is written in C, so, I ended up looking for a way to remove this (unnecessary) complexity by writing a Perl (P2) script much more user friendly when comes to working with database connections.

The query was as simple as

SELECT int1, long1, chars FROM table

which means that a simple C structure would be enough to map the entire selected fields

typedef struct rem_data
{
        int i;
	long l;
	char sz[10];
} rem_data_t;

P1 works as a TCP/IP server listening at port 7007. P2 connects to that endpoint, executes the query and packs the resulting row as a binary data packet to finally send it through the opened socket. P1 deserializes the bytes received to build the structure rem_data.

P1 is possibly long but self-explanatory :).

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

typedef struct rem_data
{
	int i;
	long l;
	char sz[10];
} rem_data_t;

typedef unsigned int _uint;
typedef void * (*dflt_func_ptr_t) (void *);

void *deserializer (void *param);
static void *listener(void *param);
pthread_t *ip_start_listener(_uint port, void * (*callback) (void *));

int main(int argc, char **argv)
{
      ip_start_listener(7007, deserializer);

      /*
       * OK, it's busy wait :D
       */ 
      for(;;);
}

void *deserializer (void *param)
{
	rem_data_t data;
	int offset		= 0;
	char *rdata		= (char *) param;

	memcpy(&data.i, &rdata[offset], sizeof(data.i));
	offset			+= sizeof(data.i);

	memcpy(&data.l, &rdata[offset], sizeof(data.l));
	offset			+= sizeof(data.l);

	memcpy(&data.sz, &rdata[offset], sizeof(data.sz));

	printf("data: i %dn", data.i);
	printf("data: l %dn", data.l);
	printf("data: sz %sn", data.sz);
}

pthread_t *ip_start_listener(_uint port, dflt_func_ptr_t callback)
{
	int sock_fd, opt = 1;
	struct sockaddr_in server;
	pthread_t *listener_thread	= NULL;

	if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
//		LOG(L_ERROR, "%s | socket(): %s", __FUNCTION__, strerror(errno));
		return NULL;
	}

	setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));

	server.sin_family 	= AF_INET;
	server.sin_port 	= htons(port);
	server.sin_addr.s_addr 	= INADDR_ANY;

	bzero(&(server.sin_zero), 8);

	if(bind(sock_fd, (struct sockaddr*)&server, sizeof(struct sockaddr)) == -1)
	{
//		LOG(L_ERROR, "%s | bind(): %sn", __FUNCTION__, strerror(errno));
		return NULL;
	}

	if(listen(sock_fd, 1000) == -1)
	{
//		LOG(L_ERROR, "ip_start_listener.listen(): %sn", strerror(errno));
		return NULL;
	}

	listener_thread		= malloc(sizeof(pthread_t));

	if (pthread_create(listener_thread, NULL, listener, (void *) ( void*[] ) { (void*) &sock_fd, (void*) callback } ) != 0)
	{
//		LOG(L_ERROR, "%s | pthread_create(): %sn", __FUNCTION__, strerror(errno));
		return NULL;
	}

	return listener_thread;
}

static void *listener(void *param)
{
	struct sockaddr_in client;
	_uint sin_size, *params		=  param;
	int client_fd, *sock_fd 	= (int *) params[0];
	dflt_func_ptr_t callback	= (dflt_func_ptr_t) params[1];

	/*
	 *  no buffering for standard output
	 */
	setbuf(stdout, NULL);
	sin_size = sizeof(struct sockaddr_in);

	for(;;)
	{
		char buffer[1024];

		if ((client_fd = accept(*sock_fd, (struct sockaddr *) &client, &sin_size)) == -1)
		{
//		    LOG(L_ERROR, "%s | accept(): %sn", __FUNCTION__, strerror(errno));
		    continue;
		}

//		LOG(L_DEBUG, "Connection from %sn",  inet_ntoa(client.sin_addr));

		read(client_fd, buffer, sizeof(buffer));

		callback(buffer);

		close(client_fd);
	}

	return NULL;
}

Compile it with

$  gcc server.c -o server -lpthread

P2 is more than simple. In case you don’t know the pack() function, here is very well explained.

#!/usr/bin/perl

use warnings;
use strict;

use IO::Socket;


my $sock 	= IO::Socket::INET->new('localhost:7007');

# Assume that the data retrieving logic is implemented here
$sock->write(pack("i l Z11", 111, 999,'hola'));

$sock->close();

And then, we just run it.

$ ./server &
$ perl pack.pl

And P1’s output should be

data: i 111
data: l 999
data: sz hola