Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re: recv in socket programming

by gone2015 (Deacon)
on Feb 12, 2009 at 13:20 UTC ( [id://743298] : note . print w/replies, xml ) Need Help??


in reply to recv in socket programming

If the far end simply stops responding, but leaves the connection open, then you are indeed in for a long wait.

BTW send and recv are really packet level operations... and may not be what you really want with TCP. In particular, $socket->recv($recv_data2,10240); is unlikely to return anything like 10K of data.

So you need to implement a time-out. You can use an ALRM signal to do this -- but it's a bit messy and won't work on all systems (notably not on Windows -- go figure). So I'd use IO::Select, as in the example below.

The example client:

use strict ; use warnings ; use IO::Socket::INET ; use Socket ; use IO::Select ; select STDERR ; $|= 1; select STDOUT ; $|= 1; my $rcvtimeo = 3 ; my $IP = 'localhost' ; my $f = 0 ; for my $f (1..6) { my $try = "Fit $f" ; my $socket = new IO::Socket::INET (PeerAddr => $IP, PeerPort => 3575, Proto => 'tcp', ) or die "Couldn't connect to Server $! ($@)\n +" ; print STDERR "Opened connection for $try\n" ; sleep(1) ; print STDERR "Sending request: $try\n" ; defined $socket->send($try) or die "send failed: $! +" ; my $wait = IO::Select->new() ; $wait->add($socket) ; for my $r (1..2) { if ($wait->can_read($rcvtimeo)) { my $recv_data ; defined $socket->recv($recv_data, 10240) or die "recv $r failed: + $!" ; print STDERR "RECEIVED-$r: '$recv_data'\n" ; } else { print STDERR "RECEIVED-$r: timed out\n" ; last ; } ; } ; close $socket ; } ;
It's worth making friends with IO::Select. Note that $s->can_read() will return a list of all the sockets associated with the IO::Select object $s, which are ready to be read from (or have outstanding errors). With only one socket it's not necessary to process that list explicitly !

The above can be run against this example server:

use strict ; use warnings ; use IO::Socket::INET ; use Socket ; select STDERR ; $|++ ; select STDOUT ; $|++ ; my $server = IO::Socket::INET->new(LocalPort => 3575, Reuse => 1, LocalHost => 'localhost', Listen => 5) or die "Couldn't create Server $! ($@)\n" ; my @ignore ; foreach my $action (2, # receive request, send 2 replies & close 1, # receive request, send 1 reply & close 0, # receive request, send 0 reply & close -1, # receive request, ignore & DON'T clos +e -2, # ignore completely & DON'T clos +e ) { my $client = $server->accept() ; if (!defined($client)) { print STDERR "Accept failed: $! ($@)" ; next ; } ; print "Connection accepted (action = $action)\n" ; my $request = 'connection' ; if ($action > -2) { defined $client->recv($request, 1024) or die "recv request faile +d $!" ; print STDERR "Request received: '$request'\n" ; } ; if ($action < 0) { push @ignore, $client ; # Not to be closed print STDERR "Ignoring $request, but leaving connection open\n" ; next ; } ; for my $r (1..2) { if ($r <= $action) { my $reply = "$request -- reply $r" ; defined $client->send($reply) or die "send reply-$r fail +ed $!" ; print STDERR "Sent '$reply'\n" ; sleep(2) ; } else { print STDERR "Not sending reply $r\n" ; } }; close $client ; print STDERR "Connection closed\n" ; } ; print STDERR "Ignoring everything" ; while(1) { sleep(10) ; print STDERR "." ; } ;