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

Hi all

I am trying to write a TCP client. The server sends responses in a proprietary format: 8-byte header, with op-codes in bytes 0-3, length in bytes of data that follow encoded in bytes 4-7, the the stated number of bytes which may be binary or ascii or a mix of both, and terminated with a linefeed.

For example, the response OFST -0.75
is encoded like this - notice the byte 7 with value 0xc == 11, for 10+1 character in the response:
810100000000000c4f465354202d302e3037350a
I started small and simple, from a code example similar to Camel 3 chapter 16 and got it basically working.

However, when the response data count happens to be 10, the byte 7 in above stream becomes 0xa.

IO::Socket::INET interprets it as end of input and returns the message truncated to the 8 header bytes:
VDIV 0.05 810100000000000
Question: can I configure IO:Socket::INET to receive data as binary and not stop receiving when it sees 0xa in the stream? How?

I did RTFM, but I failed to find the answer so far.

This is on Win2k, Perl 5.6 build 626.

Rudif
#!perl -w use strict; use IO::Socket; my $verbose = 1; print "===\n"; my $host = "127.0.0.1"; # or whatever my $port = 1861; my $remote = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => $port) or die "Couldn't connect to $host:$port\n$!\n$@\n"; my $msg = 'ofst?'; # send query $remote->send(wrap($msg)) or die "Couldn't send: $!\n$@\n"; # read the remote answer my $answer = <$remote> || '---'; print "answer: =$answer=\n"; my $response = unwrap($answer); print "response: =$response=\n"; close $remote; exit; sub wrap { my ($data) = @_; my $header = pack "C4N", hex '0x81', hex '0x1', 0, 0, length($data +); return $header . $data; } sub unwrap { my ($packet) = @_; my ($op, $hvers, $count, $data); ($op, $hvers, undef, undef, $count, $data) = unpack "C4NA*", $pack +et; { my $warn = ''; my $length = length($packet); if ($count != $length) { $warn .= "warn: $count != $length: \n"; } $warn .= unpack "H*", $packet; print STDERR "$warn\n" if $verbose; } return $data; } __END__

Edited by footpad, 23 June 2001 - 20:46 (PDT)

Replies are listed 'Best First'.
Re: IO:Socket::INET - how to receive binary data
by bikeNomad (Priest) on Jun 24, 2001 at 04:43 UTC
    Your problem is that you're using <> to read line by line:
    my $answer = <$remote> || '---';

    Since your header can contain (multiple!) \n characters, you can't use <> to get it (you may be able to use <> for the data, depending on whether it has embedded \n characters). Instead of using <>, you want to use read or recv to get first the header, then the remaining bytes. You can forget about the linefeed, since you have a count:

    my $header; $remote->read($header, 8); ($op, $hvers, undef, undef, $count) = unpack "C4N", $header; my $data; $remote->read($data, $count);

    If you want to mix the read idioms (per tye's suggestion) (of course, this assumes you can't have embedded \n in your data), you can do it this way:

    my $header; $remote->read($header, 8); ($op, $hvers, undef, undef, $count) = unpack "C4N", $header; my $data = <$remote>;

    update: changed to mention read and show mixture of idioms (thanks tye!), attribute to tye correctly

      You can also just do a sysread() for binary data, if you're not going to mix it with buffered reads/writes. I would add a test for the linefeed afterwards, to detect the case where the connection drops in the middle.
      --
      Snazzy tagline here

        I prefer to use read over sysread and recv because it works with buffered I/O operations while the other two don't. Though if you are using UDP in certain ways, then you might want to use recv so you can get the source address for each packet. In rare cases, the "flags" argument to recv can also come in handy so that you can MSG_PEEK at the data without reading it or check for out-of-band data.

                - tye (but my friends call me "Tye")
      Thank you bikeNomad.

      I adopted your first solution and it works neatly. Also your logic matches neatly the protocol.

      I ought to try alternatives suggested by other monks and get the understanding of tradeoffs.

      Rudif