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

Monks, I have a bug, and need some assistance correcting it. Here's what I'm trying to do. client sends a variable length message to server (encrypted) server reads message, does Stuff (tm) to with it and responds via the same socket. My problems is that occasionally (1/3ish of the time) the message from the client is truncated. Here are the relevant code bits:

CLIENT:

use Socket qw(:DEFAULT :crlf); use IO::Handle; my ($remote, $iaddr, $paddr, $proto, $line, $response); $remote = $client || 'localhost'; $port = $port || 8086; # random port if ($port =~ m/\D/) { $port = getservbyname($port, 'tcp') } die "Bad port" unless $port; $iaddr = inet_aton($remote) or die "can't connect: $remote"; $paddr = sockaddr_in($port, $iaddr); $proto = getprotobyname('tcp'); socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; connect(SOCK, $paddr) or die "connect: $!"; SOCK->autoflush(1); print SOCK $encrypted_message . CRLF;

SERVER:

use Socket qw(:DEFAULT :crlf); use IO::Handle; next unless my $rem_addr = accept(SESSION,SOCK); my ($r_port,$r_addr) = sockaddr_in($rem_addr); warn "Connection from [", inet_ntoa($r_addr),",$r_port]\n"; SESSION->autoflush(1); my $incoming = ''; my $line = ''; while( defined($line = <SESSION>)) { $incoming .= $line; last if $incoming =~ /\n$/; # vain attempt to find end of line } chomp $incoming; my $msg = decrypt($key, $incoming);
and so on . . I can't seem to reliably detect the end of the message. Any advice would be greatly appreciated.

-anelson

Replies are listed 'Best First'.
Re: Socket IO problems
by pg (Canon) on Nov 23, 2002 at 05:12 UTC
    When you read from TCP socket, you may receive the message in pieces(, but they are in the right order). Do multiple reads, until you detect the predeined end of msg. This is normal.

    Update:

    I tested with three different ways to read from a socket, read, sysread, and recv, and found read actually gave you a much better chance to recv the whole packet in one call.

    use strict; use IO::Socket; my $server = IO::Socket::INET->new(Proto => "tcp", PeerPort => 80, PeerAddr => "www.yahoo.com", Timeout => 2000) || die "failed to connect\n"; my $req = "GET / HTTP/1.1\r\nHost: www.yahoo.com\r\n\r\n"; print $server $req; my $count = 0; while (1) { my $req; #$server->recv($req, 700000); read($server, $req, 700000); #sysread($server, $req, 700000); print "packet rcvd:\n", substr($req, 0, 20), "\n"; print "packet count = ", ++ $count, "\n"; exit if ($req eq ""); }
Re: Socket IO problems
by dpuu (Chaplain) on Nov 23, 2002 at 05:09 UTC
    is $encrypted_message binary? if so, it might contain your EOL marker. An easy solution is to send the length first, and then read that number of bytes:
    # client print SOCK length($msg) . "\n" . $msg; #server: chomp( $len = <SOCK> ); read(SOCK, $msg, $len );
    --Dave
Re: Socket IO problems
by anelson (Acolyte) on Nov 23, 2002 at 06:12 UTC
    Using the length() solution at the moment. Seems to have nipped the problem in the butt. Running a test loop on it now with 2megs of random goodness (*hugs* to /dev/random) and it seems to be behaving. The 1000 iterations of my test should be done by morning hehe. Thanks for the input, monks!
    -anelson