http://qs1969.pair.com?node_id=116546

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

Hi everyone, i am almost ready with the MSN perl module that i was talking here a few days ago, but since it is my first program with sockets, i have a little problem, this subroutine gets the server answers of my request, usually the server answer with just one line, and this sub works just fine, but if i modify it to get more than one lines it don't do anything :( The modifications that i've tried are:
change
my $line = <$sock>;
to
my @lines = <$sock>;
And it didn't work , and the other was
while ( my $line = <$sock> ) { #more stuff }

With any of the 2 changes it doesn't work
Any help.
Thanx all
sub sendCommands{ if (@_) { print $sock "$_[0]\r\n"; #$sock is a socket to the server my $line = <$sock>; print "Client: $_[0]\r\nServer: $line" if $DEBUG; my @values = split(/ /,$line); return @values if ( $line ) } }


Dreams they just disapear into the shadows,
then they become true....

Replies are listed 'Best First'.
Re: Getting Data from server
by pjf (Curate) on Oct 04, 2001 at 05:18 UTC
    G'day shadox,

    When you say, "it doesn't do anything", does that really mean "it hangs forever?". If so, I suspect that I know what the problem is.

    If your socket is in the (default) blocking mode, your process will block waiting for data whenever you try to read from the socket. If there is a line waiting for you, or one is just about to arrive, then this works wonderfully. However, if you try to read when the remote end isn't intending on sending you anything, your process will sit there waiting for data that will never arrive.

    You've got a few options here. Probably the best way of solving things is to look at the data you've received and determine if you're expecting any more. So if you know that every response from the client will end with a "GO AHEAD" you can do something like this:

    my $buffer = <$sock>; while ($buffer !~ /GO AHEAD\Z/) { $buffer .= <$sock>; }
    Of course, the catch here is that if the remote end doesn't terminate its response as you expect, you're left blocking on the socket forever.

    Your second option is to use some sort of timeout, possibly in conjunction with the terminator matching code above. If we're expecting another line, we wait for one to arrive, but we don't block indefinitely. select() is a good way to wait for data to arrive, but allowing you to get out cleanly if it takes too long.

    The problem with select is that it doesn't play nicely with buffered I/O, which is what <$socket> gives you. Instead, you'll have to use low-level functions like sysread. This isn't much harder. I'd strongly recommend using IO::Select rather than the core select() call, for sanity reasons:

    use IO::Socket; use POSIX qw/BUFSIZ/; # Create an IO::Select object, and ask it to look at $sock. my $s = IO::Select->new(); $s->add($sock); # Continue to read on $sock until things are quiet for # at least one second. my $message; while ($s->can_read(1)) { my $buffer; my $bytes_read = sysread($sock,$buffer,BUFSIZ); unless ($bytes_read) { # Socket just closed, clean it up... } $message .= $buffer; }
    Select can be used to examine multiple filehandles and determine which one(s) are available for reading. See the IO::Select manpage for details.

    The downside of using select like this is that your process will be spending a second at the end of each message waiting for data that isn't there, or it could miss data in caes of high lag where it takes longer than a second for packets to arrive.

    I would personally recommend using the first method of determining when more data is due to arrive, but throwing in a long timeout to catch any "improvements" to the protocol that you don't know about.

    Your third option is to go to non-blocking sockets, which will increase your code complexity significantly. If your program needs to be dealing with multiple clients at once, or never wants to be blocking waiting for I/O, then non-blocking sockets are useful. If you only expect to be dealing with a single connection, and you don't have other things to do while waiting for that connection, you definitely want to go with blocking I/O.

    Hope this solves the problem.

    Cheers,
    Paul