Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Making IO::Socket::UNIX client time out

by kroach (Pilgrim)
on Oct 02, 2019 at 12:53 UTC ( #11106953=perlquestion: print w/replies, xml ) Need Help??

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

I'm trying to make a IO::Socket::UNIX client connection time out after some time if the server is too slow to respond. I tried using the Timeout argument from IO::Socket but it doesn't appear to work in this case. Here is a server/client example:

#!/usr/bin/perl use strict; use warnings; use IO::Socket::UNIX; use constant SOCKET => 'test_socket.sock'; my $mode = shift or die "Specify a mode\n"; if ($mode eq 'server') { my $server = IO::Socket::UNIX->new( Type => SOCK_STREAM, Local => SOCKET, Listen => 1, ) or die $!; while (my $conn = $server->accept()) { sleep 5; my $name = <$conn>; print {$conn} "Hello $name"; close $conn; } } elsif ($mode eq 'client') { my $client = IO::Socket::UNIX->new( Type => SOCK_STREAM, Peer => SOCKET, Timeout => 2, ) or die $!; print {$client} 'John', "\n"; print "Got reponse: ", scalar <$client>; close $client; } else { die "Unsupported mode: $mode\n"; }

The client is set to time out after 2 seconds and the server to take at least 5 seconds to respond. I expected the client to stop after 2 seconds, however it just reads the response after 5 seconds as if the Timeout was completely ignored. Am I doing something wrong?

There is a solution using alarm() in perlipc which works for any kind of blocking code:

my $ALARM_EXCEPTION = "alarm clock restart"; eval { local $SIG{ALRM} = sub { die $ALARM_EXCEPTION }; alarm 10; flock(FH, 2) # blocking write lock || die "cannot flock: $!"; alarm 0; }; if ($@ && $@ !~ quotemeta($ALARM_EXCEPTION)) { die }

However, there is probably a cleaner way to do it. Any ideas? What's the recommended way to make a socket client timeout?

EDIT: Solution

It's possible to use setsockopt to set the read timeout:

use POSIX qw[ ETIMEDOUT EWOULDBLOCK ]; use Socket qw[ SOL_SOCKET SO_RCVTIMEO ]; my $client = IO::Socket::UNIX->new( Type => SOCK_STREAM, Peer => SOCKET, ) or die $!; my $timeval = pack 'l!l!, 2, # seconds 0; # microseconds $socket->setsockopt(SOL_SOCKET, SO_RCVTIMEO, $timeval) or die $!; print {$client} "John", "\n"; my $response = <$client>; die 'Timeout' if !$response and $! == ETIMEDOUT || $! == EWOULDBLOCK; ...

In order to set a write timeout, replace SO_RCVTIMEO with SO_SNDTIMEO. This is exactly what IO::Socket::Timeout does, I'm leaving a pure solution here for educational purposes, in case someone stumbles upon this thread in the future.

Replies are listed 'Best First'.
Re: Making IO::Socket::UNIX client time out
by shadowsong (Pilgrim) on Oct 03, 2019 at 00:03 UTC

    Hi kroach

    What you're looking for is IO::Socket::Timeout.

    See below for a bastardised version of your code that uses it (change $CLIENT_WAIT to 2 and you'll see the server response).

    Good luck,
    Shadow

Re: Making IO::Socket::UNIX client time out
by Fletch (Chancellor) on Oct 02, 2019 at 13:13 UTC

    Nothing in the documentation for IO::Socket::UNIX says it takes a Timeout option; IO::Socket::INET does, but not the unix domain flavour nor its parent IO::Socket. That it doesn't produce some sort of warning on unsupported / unexpected arguments is . . . unexpected.

    Update: Aaah, that's because (after RTFS) despite not being documented IO::Socket does handle Timeout. Hrmm, never mind me.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      IO::Socket does handle Timeout, what's more, it actually works in the server part of IO::Socket::UNIX. This server will stop if it doesn't receive a connection within 2 seconds:
      #!/usr/bin/perl use strict; use warnings; use IO::Socket::UNIX; use constant SOCKET => 'test_socket.sock'; my $mode = shift or die "Specify a mode\n"; if ($mode eq 'server') { my $server = IO::Socket::UNIX->new( Type => SOCK_STREAM, Local => SOCKET, Listen => 1, Timeout => 2, ) or die $!; while (my $conn = $server->accept()) { my $name = <$conn>; print {$conn} "Hello $name"; close $conn; } print 'Timed out', "\n"; } elsif ($mode eq 'client') { my $client = IO::Socket::UNIX->new( Type => SOCK_STREAM, Peer => SOCKET, ) or die $!; print {$client} 'John', "\n"; print "Got reponse: ", scalar <$client>; close $client; } else { die "Unsupported mode: $mode\n"; }
Re: Making IO::Socket::UNIX client time out
by Don Coyote (Friar) on Oct 02, 2019 at 13:17 UTC

    Just a thought, the sleep function is placed after the server accept, so possibly its returning after five seconds because the connection has been made, however you designed lag into the process. After the lag has cleared, the connection can continue and return.

    Did you try sleeping before the server accept?

    # sleep 5; # while (my $conn = $server->accept()) {

    Assuming this is the case, it would depend a lot on how server implementation actually occurs. The sleep introduced after the connect would be implementing some kind of error that would likely return an error code after a short elapse of time.

    The sleep placed prior, may simulate better something like multiple connection requests, such that the client is actually waiting in a queue to connect. Again it depends a lot on the system implementation.

      I did try sleeping before accept, the result is the same. The client just waits patiently and gets the response after 5 seconds despite Timeout.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11106953]
Approved by marto
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2020-10-31 04:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My favourite web site is:












    Results (286 votes). Check out past polls.

    Notices?