http://www.perlmonks.org?node_id=718645

muthuma has asked for the wisdom of the Perl Monks concerning the following question:

Client side contains Array of Hash how can it be sent to server side for data retrieval. Below code gives stub and it is not correct but provide network connection.
SERVER use strict; use IO::Socket; use Sys::Hostname; use constant BUFSIZE => 1024; my $host = hostname; my $port = shift || '10280'; my $socket = new IO::Socket( Domain => PF_INET, Proto => getprotobyname('tcp'), LocalAddr => $host, LocalPort => $port, Listen => 1,#SOMAXCONN, #ReuseAddr => SO_REUSEADDR, ) or die $@; my $buffer; print "Waiting to do service...\n"; while (my $client =$socket->accept) { print "Client: ",$client->peerhost," Connected..\n"; syswrite($client,"Reached Server\n",BUFSIZE); my @AoH = (); if(sysread($client,$buffer,BUFSIZE)>0) { @AoH = unpack("a*",$buffer); print "AoH:",@AoH,"\n"; print $#AoH,"<= Length\n"; for my $i ( 0 .. $#AoH ) { print "Id = $i => {\n"; for my $param ( keys %{ $AoH[$i] } ) { print "\t$param=$AoH[$i]{$param}\n"; } print "}\n"; } } else { print "Client Disconnected..\n"; print "Waiting to do service...\n"; } print "Again to accept connection\n"; } CLIENT use strict; use IO::Socket; use constant BUFSIZE => 1024; my @AoH = ( { husband => "barney", wife => "betty", son => "bamm bamm", }, { husband => "george", wife => "jane", son => "elroy", }, { husband => "homer", wife => "marge", son => "bart", }, ); my $host = shift or die "Usage: client.pl host [port]\n"; my $port = shift || '10280'; my $socket = new IO::Socket(Domain => PF_INET, PeerAddr => $host, PeerPort => $port, Proto => getprotobyname('tcp'), Timeout => 60,) or die $@; my $buffer; if (sysread($socket,$buffer,BUFSIZE) > 0) { syswrite(STDOUT,$buffer); } print "AoH=",@AoH,"\n"; print "\t$param=$AoH[$i]{$param}\n"; # How to send the Array of Hash in one shot=> modification required in + below line #******** Monks can you pls suggest good idea??? syswrite($socket,pack("a*",@AoH),BUFSIZE);# only address is sent.. close($socket);

Replies are listed 'Best First'.
Re: Send an Array of Hash in Socket
by ikegami (Patriarch) on Oct 22, 2008 at 08:59 UTC

    To put the earlier answers in context, the only thing you can send over a socket is bytes. Sockets are file handles, and (system) file handles can only handle streams of bytes. That means you need convert your data into a stream of bytes that contains enough information for the received to reconstruct the data. The process of converting the data to a stream of bytes is called serialization. The process of recreating the data from the stream of bytes is called deserialization.

    Storable is a module used to serialize arbitrary Perl data structures. I think it produces rather compact strings. pack/unpack basically reinvent the Storable wheel, but can be used to produce custom solution such as something even more compact or something that matches an existing serialization method.

    Storable is not so good if you need to interface with something other than Perl. That's why JSON was mentioned. It's a well defined format that has been ported to many languages and will handle fairly complex structures. Instead of being compact, it's human readable. XML and YAML also share all those characteristics.

    One advantage of Storable is that it handles embedding the serialized data in the middle of a data stream natively. In most other cases, you'll need some kind of framing to signal the end of the data structure if you intend to keep using the socket. This is often done by preceding the serialized data with the size in bytes of the serialized data.

    # Sender: my $serialized = serialize($data); print $sock pack( 'N', length($serialized) ), $serialized;
    # Receiver: my $data = do { my $length = do { my $buf; sysread( $sock, $buf, 4-length($buf), length($buf) ) while length($buf) < 4; unpack( 'N', $buf ) }; my $buf; sysread( $sock, $buf, $length-length($buf), length($buf) ) while length($buf) < $length; deserialize($buf) };
Re: Send an Array of Hash in Socket
by andreas1234567 (Vicar) on Oct 22, 2008 at 08:06 UTC
    I'd use a text-based format for data interchange, such as JSON. Read The Importance of Being Textual (The Art of Unix Programming, by Eric Steven Raymond, 2003) for a rationale.

    Server code:

    Client code: Running the client:
    $ perl client.pl localhost Reached Server
    Running the server:
    $ perl server.pl Waiting to do service... Client: 127.0.0.1 Connected.. AoH: $VAR1 = [ { 'son' => 'bamm bamm', 'wife' => 'betty', 'husband' => 'barney' }, { 'son' => 'elroy', 'wife' => 'jane', 'husband' => 'george' }, { 'son' => 'bart', 'wife' => 'marge', 'husband' => 'homer' } ];
    --
    No matter how great and destructive your problems may seem now, remember, you've probably only seen the tip of them. [1]
Re: Send an Array of Hash in Socket
by BrowserUk (Patriarch) on Oct 22, 2008 at 08:07 UTC
Re: Send an Array of Hash in Socket
by Corion (Patriarch) on Oct 22, 2008 at 07:59 UTC

    pack and unpack are your friend if you need to transfer just lists of strings:

    my @items = qw(muthuma corion foo); my $template = '(N/a*)*'; my $packed = pack $template, @ARGV; print ">>$_<<" for unpack $template, $packed;

    Update: BrowserUk's suggestion of Storable is far better than my idea of pack and unpack, because it handles arbitrary datastructures. Just make sure you use nstore so you are independent of the byte order of the machines.

Re: Send an Array of Hash in Socket
by muthuma (Novice) on Oct 22, 2008 at 08:53 UTC
    Thanks to all three of you for your valuable replies.