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

I have recently started to expand my Perl horizons into network/sockets programming, with the assistance of Dr. Stein's Network Programming with Perl. I'm shocked and embarrassed about how I've managed to get by with only a very fundamental understanding of networking protocols and TCP/IP for all these years, and now I'm faced with a stumper...

I am trying to write a script that will help me examine the contents of a mainframe database via an interface with a mysterious, black-box server that is off-site. No one seems to know anything about this server and what, exactly, it is expecting as far as message formats.

What I do have is a bunch of Java code that is behaving nicely. I hope to interpolate my own script from these heaps of Java, but I am having problems right from the beginning. The part of the Java code that is perplexing me looks like this:

OutputStream stream = s.getOutputStream(); if (stream != null) { DataOutputStream output = new DataOutputStream(stream); if (output != null) { output.writeShort(0); output.writeShort(0); output.writeShort((short) theData.length()); output.writeBytes(theData); output.flush(); res = true; } }

In this code, s is a java Socket instance that is part of the class that wraps the Socket. Another method reads from the sockets InputStream/DataInputStream. theData is a formatted string/messsage.

I understand the streams (or at least I think I do), but what's the story with the writeShort and writeBytes methods, which I am trying to emulate w/ Perl? I see from the Java docs that writeShort "Writes a short to the underlying output stream as two bytes, high byte first." and that writeBytes "Writes out the string to the underlying output stream as a sequence of bytes. Each character in the string is written out, in sequence, by discarding its high eight bits." Errrmmm... okay.

My own attempt at doing something similar looks like the following:

use strict; use warnings; use IO::Socket; my $host = '198.81.233.12'; #not real IP address my $port = 1302; #not real port my $data = '003 0000000073223 3000 +0073223091706407 20040513023'; my $msg_out = pack("nnna*", 0, 0, length $data, $data); print "\$msg_out = -$msg_out-\n"; my $socket = IO::Socket::INET->new( PeerAddr => $host, PeerPort => $port ) or die "Can't connect: $!\n"; print $socket $msg_out; my $msg_in = <$socket>; print "->$msg_in\n"; $socket->close or warn $@;

This code isn't even getting response from the socket... it never gets to the statement print "->$msg_in\n". I'm sure that I have the correct IP address and port. I chose the pack format 'nnna*' because I thought it most closely matched what the java methods were doing, but then again what do I know? I see from the perldocs that n handles a short in big-endian order, and that a handles a null-padded string. I've tried other formats, too, w/o success.

The string in $data is an exact copy of a string that I know will return a response in the java code. I should receive a long string which I can unpack or substr into individual elements.

Can anyone tell me where I am going wrong? Is it the pack format? Should I even be trying to pack the data? Am I amazingly clueless?

Hanlon's Razor - "Never attribute to malice that which can be adequately explained by stupidity"

Replies are listed 'Best First'.
Re: Converting Java Sockets to Perl Sockets
by dave_the_m (Monsignor) on May 13, 2004 at 15:30 UTC
    You're probably sending the right data (you could confirm this with a packet sniff of the Java and Perl sessions; but your error is probably in the reading back part: <$socket> will attempt to read a line of data from the socket; if the returned data isn't character based (which is probably isn't), then this is likely to hang waiting for a \n character. Instead, you need to look at the Java code that does the reading back, and determine what it is expecting, then read that number of bytes in.

    For example the server might return 2 bytes indicating the length, followed by that many bytes of data. That could be read using

    my $buf; read($socket, $buf,2); my $len = unpack 'n', $buf; read($socket, $buf, $len);
Re: Converting Java Sockets to Perl Sockets
by Art_XIV (Hermit) on May 13, 2004 at 15:42 UTC

    Thanks for everyone's help:) Adding a read did the trick, and now $buf is filled with the stuff that I want!

    use strict; use warnings; use IO::Socket; my $host = '199.82.244.14'; my $port = 13001; my $data = '003 0000000073223 3000 +0073223091706407 20040513023'; my $msg_out = pack("nnna*", 0, 0, length $data, $data); print "\$msg_out = -$msg_out-\n"; my $socket = IO::Socket::INET->new( PeerAddr => $host, PeerPort => $port ) or die "Can't connect: $!\n"; print $socket $msg_out; my $buf; read($socket, $buf,6); my ($tag,$code,$len) = unpack 'nnn', $buf; print "\$tag = '$tag'\n"; print "\$code = '$code'\n"; print "\$len = '$len'\n"; read($socket, $buf, $len); print "\$buf = '$buf'"; $socket->close or warn $@;
    Hanlon's Razor - "Never attribute to malice that which can be adequately explained by stupidity"
Re: Converting Java Sockets to Perl Sockets
by matija (Priest) on May 13, 2004 at 15:21 UTC
    Try to syswrite to socket instead of printing to it, or at least do $socket->autoflush(1). That way you'll know you're not suffering from buffering.

    And just for the first test, snoop what the java client is sending, construct a string that matches that exactly (using the \xhh notation), and send that. Yes, it's not maintainable, but it will limit the number of things you're trying to debug at the same time.

      While your advice won't hurt, I doubt it will solve the problem. Sockets are unbuffered by default so neither of your suggestions should matter.

      However, trying to use <$socket> is very often a problem. It will not return, no matter how long you wait, unless $/ ("\n") or end-of-file arrives on the socket.

      Switching to using read or sysread or even recv will likely fix the problem.

      - tye