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