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

Dear Monks,

I'm trying to play with timeouts in the module IO::Socket. Here's what I got so far.

server.pl:

use 5.010; use strict; use warnings; use IO::Socket; say 'Server listening...'; my $server = IO::Socket::INET->new(Proto => 'tcp', LocalPort => 8000, Listen => 1, ReuseAddr => 1, Timeout => 10) or die; my $client = $server->accept() or die; $server->close; say 'Connection from ' . $client->peerhost;

client.pl:

use 5.010; use strict; use warnings; use IO::Socket; my $socket = IO::Socket::INET->new(Proto => 'tcp', PeerAddr => 'localhost', PeerPort => 8000, Timeout => 10) or die; say 'Client connected';

I launch server.pl, followed shortly by client.pl. I got on the server side:

Server listening... Connection from 127.0.0.1

And on the client side:

Client connected

Now, I launch only server.pl. I got 10 seconds later:

Server listening... accept: timeout ...propagated at server.pl line 16.

So far, so good. Now I launch only client.pl. And I instantly got:

IO::Socket::INET: connect: 10061        ...propagated at client.pl line 8.

I would have preferred that the client gave up after 10 seconds.

Is there a way to make the client wait a bit for the server to start up instead of giving up immediately? (I'm on Windows by the way)

Stamm

Replies are listed 'Best First'.
Re: Timeouts in IO::Socket (updated)
by haukex (Archbishop) on Jun 23, 2019 at 13:14 UTC

    When you launch client.pl on its own, it tries to connect to TCP port 8000, but there is nothing listening, so the server OS immediately responds with a TCP packet with the RST flag set (at least on my Linux system), which results in the TCP connection attempt to abort with the error "Connection refused" - the timeout doesn't apply because there is a response coming from the server, albeit a negative one. On Windows, the error code 10061 is WSAECONNREFUSED - from here: "No connection could be made because the target computer actively refused it. This usually results from trying to connect to a service that is inactive on the foreign host—that is, one with no server application running."

    You could implement the retry yourself, the following works for me, you could adapt this as you like:

    use warnings; use strict; use IO::Socket; my $socket; for (1..10) { $socket = IO::Socket::INET->new(Proto => 'tcp', PeerAddr => 'localhost', PeerPort => 8000, Timeout => 10); last if defined $socket; print "Not connected, retrying...\n"; sleep 1; } die "Connection failed" unless defined $socket; print "Client connected\n";

    Updated: Minor tweaks to text and code.

    Update 2: Actually, the above code can wait for up to 100 seconds total (10 times the Timeout value). You might want to use a time-based loop instead, in the following I've also made the logic and messages a little bit nicer:

    use warnings; use strict; use IO::Socket; my $CONN_TIMEOUT_SEC = 10; my $socket; my $startt = time; my $attempt = 1; while ( time - $startt < $CONN_TIMEOUT_SEC ) { sleep 1 if $attempt > 1; print "Connection attempt ", $attempt++, "...\n"; $socket = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => 'localhost', PeerPort => 8000, Timeout => $CONN_TIMEOUT_SEC); last if defined $socket; } die "Connection failed" unless defined $socket; print "Client connected\n";

      Ok, I didn't know the inner workings of a connection.

      I ended up using your first example, with the sleep but without the timeout.

      It works like a charm, thank you very much! :-)

      Stamm