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

Hello, I'm writing a small app that sends a message to a server via TCP/SSL and then I need to wait for a full message back from the system. Can anyone see where I can improve the line of "sleep". I tried something using an EVAL, i read this on the internet somewhere, but it doesnt really seem to work.

Any other ideas? Thanks!

#!/usr/local/bin/perl use strict; #Create Socket Connection use IO::Socket::SSL qw(debug3); my $sock = IO::Socket::SSL->new( PeerAddr => 'LOCALHOST', PeerPort => '6999', SSL_version => 'SSLv3', SSL_cipher_list => 'COMPLEMENTOFDEFAULT', SSL_error_trap => 1 ); die "Could not create socket: " . IO::Socket::SSL::errstr() unless $so +ck; #Get the message my $socket_message = shift; print "Message Rec: $socket_message\n"; #Convert the hex message to a string $socket_message = (hex2string($socket_message)); print "Translated Message: $socket_message\n"; print "*Sending Message*\n"; print $sock $socket_message; print "*Message Sent*\n"; my $response; sleep 4; #I wish there was a better way to do this! #This times out the function so I can get everything from the server. $SIG{ALRM} = sub { die "timeout" }; eval { alarm(1); #Change this 1 to something else for more time. while(1){ sysread($sock, $response, 4000); } alarm(0); }; close($sock); print "Server Response: $response\n"; $response = (string2hex($response)); print "Server Response: $response\n"; print "Transaction Completed!"; #Hex Functions sub string2hex { my $in = shift; join(' ', map { sprintf("%02X", ord($_)) } split(//, $in)); } sub hex2string { my $in = shift; join('', map { &convert_word($_) } split(/\s+/, $in)); } sub convert_word { my $word = shift; my $rv = ''; if (length($word) == 2) { $rv = chr(hex($_)); } elsif (length($word) == 1) { $rv = $_; } $rv; } exit;

Replies are listed 'Best First'.
Re: Waiting for input, without sleep
by jrsimmon (Hermit) on Jun 30, 2009 at 14:54 UTC
    From cpan IO::Socket::SSL :

    peek(...)

    This function has exactly the same syntax as sysread(), and performs nearly the same task (reading data from the socket) but will not advance the read position so that successive calls to peek() with the same arguments will return the same results. This function requires OpenSSL 0.9.6a or later to work.


    pending()

    This function will let you know how many bytes of data are immediately ready for reading from the socket. This is especially handy if you are doing reads on a blocking socket or just want to know if new data has been sent over the socket.


    Update:
    Fixed cpan link. ikegami++
      Neither of these work, it doesn't wait for input from the server at all.... $sock->peek($sock, $response, 4000);
        Yes, peek() and pending() are intended as non-blocking calls that you can use to get an idea of what will happen if you issue a read().

        If I understand your question correctly, you desire a more efficient way to wait until the response from the server is available. This is typically done by:
        while($data = $sock->peek()){ if($data eq $what_you_want){ &DO_YOUR_STUFF; last; }else{ sleep 1; } }

        It's not totally necessary to have that short sleep, but typically you want some type of pause so that your script doesn't get into a tight loop and hog the cpu.

        If you don't know what data you expect back, you can use pending() instead of peek() to see the amount of data waiting to be read().
Re: Waiting for input, without sleep
by wol (Hermit) on Jun 30, 2009 at 17:10 UTC
    There are a couple of odd things about your code:

    If you use sysread without an offset parameter, then it will always read into the start of your buffer. That means that if your server's response happened to turn up in more than one packet, you might trash the first bit of content with the second. I guess you might not see this if the responses are small enough to fit in one packet, but that's more by luck than judgement, especially if the data could be up to 4000 bytes in length.

    Your algorithm for reading the response is a bit odd:

    1. Wait 4s for server to do its thing
    2. Enter a loop, reading anything that turns up (but always overwriting the start of the buffer, as noted)
    3. Abort the loop after 1s, no matter what (if anything) arrived
    4. Close socket
    5. Hope that whatever arrived is OK
    You might want to consider either:
    • Waiting for 5s, and doing a single read to catch everything that's turned up
    • Waiting for 5s, and looping until sysread returns nothing
    • Looping until the server closes the socket (at which point I think sysread may return 0 or undef - need to experiment).

    --
    use JAPH;
    print JAPH::asString();

Re: Waiting for input, without sleep
by pileofrogs (Priest) on Jun 30, 2009 at 19:50 UTC

    I bet I know what's up here. I had the exact same symptoms not long ago.

    Does your message end with a "\n"? If not, your reader doesn't know the message has ended. (You don't have to use \n, set $\ on both server and client. see perlvar).

    Hope that helps.

    --Pileofrogs

Re: Waiting for input, without sleep
by ikegami (Patriarch) on Jun 30, 2009 at 15:54 UTC

    I need to wait for a full message back from the system.

    What's the format of a message?

      Its just a string that comes back in, however there is one section of information I get back that is of variable length. This is why I have it set to the high 4000.
        That doesn't answer anything. Strings are objects consisting of a length and a series of characters. How do you encode these (individually if applicable, and as a whole)?