in reply to Socket read timeout on Windows

For some reason, this website has been having trouble staying up. But it is working at the moment. I will attach a client/server pair that I wrote some time ago. This shows one way of using the alarm signal to timeout read requests to the server. The big problem with this approach is that once you get a signal, there is not much you can do about it other than clean up some stuff and die. But often that is enough. See if this approach will work for you.

There are other ways where it is possible to check if data is available on the pipe before attempting to read so that you don't hang. I have another project competing for my time right now. So can't provide an example of that right away.

#!/usr/bin/perl # Simple client for testing # https://www.perlmonks.org/?node_id=11151665 use strict; use warnings; $!=1; use IO::Socket; my $socket = IO::Socket::INET -> new (PeerAddr => 'localhost', PeerPort => 8081, Proto => 'tcp', Type => SOCK_STREAM) or die "Cannot open socket!"; print "$socket"; my $cmd = <<'END_MESSAGE'; GET /DATA_String/{300 continuous characters of sensor data}HTTP/1.1 Some host line Some Connection line Some user-agent line, next line is blank END_MESSAGE $SIG{PIPE} = 'handler'; $SIG{INT} = 'handler'; $SIG{QUIT} = 'handler'; $SIG{ALRM} = 'handler'; server_request($cmd); #single request close $socket; #hang up exit(); sub server_request { my $cmd = shift; print "sending CMD to server\n"; alarm(2); print $socket "$cmd"; $socket->flush(); alarm(0); print "client debug: back from server: \n"; while (alarm(2),my $sResponse = <$socket>) { alarm(0); print "from Server: $sResponse"; } alarm(0); } sub handler { my ($signo) = shift; if ($signo eq "INT" or $signo eq "QUIT") { print $socket "QUIT\n"; close ($socket); exit (1); } elsif ($signo eq "ALRM") { print "server too slow - request timed out!\n"; exit (2); } else #SIGPIPE (probably!) { print "Server died with signal $signo\n"; exit (3); } }
#!/usr/bin/perl # Perl SOPW # https://www.perlmonks.org/?node_id=11151665 use strict; use warnings; $|=1; #Turn stdio buffering off use IO::Socket; use POSIX qw(sys_wait_h); use constant LISTEN_PORT => 8081; my $socket = IO::Socket::INET->new ( LocalPort => 8081, Proto => 'tcp', Type => SOCK_STREAM, Reuse => 1, Listen => 10, ) or die "Could not open port 8081!"; my $client; while (1) { $client = $socket->accept() || next; #accept() is not re-entrant #loop if error my $pid = fork(); die "Bad Fork!" unless defined $pid; if ($pid == 0 ) # child { close ($socket); # children don't listen for new connections my $line; my @buff; while (defined ($line=<$client>) and $line !~ /^\s*$/) { push @buff, $line; } my $time = time(); print "INCOMING from a Client Connection at $time\n"; do_command(\@buff); exit(0); } else # parent { close ($client); #wait for next client request } } sub do_command { my $buff = shift; print $client "START\n"; print $client "$_" for @$buff; #just loopback lines to client print $client "END\n"; $client->flush(); }

Replies are listed 'Best First'.
Re^2: Socket read timeout on Windows
by cavac (Prior) on Feb 17, 2025 at 15:14 UTC

    Some random tips from problems i had to handle frequently:

    *) Depending on the network settings (especially if there is a firewall/NAT inbetween), it might also be required to send some "empty" No-OP commands every now and then to prevent the NAT from silently dropping the connection mapping.

    *) Sending No-OP commands can also help detect if someone has turned off¹ the equipment on the other side. If there is no dedicated "no operation" command if a specific protocol, there usually is some sort of short "device status" request.

    *) There is technically the option to periodically send TCP packets without a payload too keep-alive a connection. Never tried it in Perl, because it doesn't save that much traffic and it doesn't allow "full stack" testing of the connection (e.g. "the connection is still open, but we still don't know if the application on the other side is properly handling incoming requests").


    And now something a bit more off-topic

    ¹ "Do not turn off" stickers on critical equipment don't work if someone can still push the power button.

    Also make sure that cleaning personal can't unplug critical devices. I had more than one stress test over the weekend ruined because someone decided that they didn't know if the free power sockets work but the one my device was plugged into surely do...

    And there was that one time one of my fellow brainiacs unplugged a POE-powered access point from the wall socket and plugged in his laptop instead. He didn't want to bother finding out if any of the other ethernet wall sockets were properly patched, but the access point must have internet, mustn't it? Result: WiFi was offline for a few minutes until we plugged it back in. The laptop on the other hand never worked again after releasing some magic smoke straight through the keyboard. Ah, the joy of cheap, non-intelligent POE injectors. You know the kind that provides 48V with proper oompf no matter what's plugged in on the other side...

    PerlMonks XP is useless? Not anymore: XPD - Do more with your PerlMonks XP
    Also check out my sisters artwork and my weekly webcomics
Re^2: Socket read timeout on Windows
by bonzi (Acolyte) on Feb 15, 2025 at 09:24 UTC

    Ah, the monastery is reachable again!

    Thank you - if everything else fails, I will try this approach. It might be possible just to break all connections, re-establish them, and start over - we shall see.

    But why does IO::Socket::INET ignore the read timeout setting? As far as I can see, connect timeout works, and we can see from "ordinary" Windows apps (e.g. browsers) that it is possible to time out on TCP read on Windows. Well, perhaps they too jump through hoops like this.

      Yeah, I dunno why the Monastery has been unreachable at times lately.

      This whole business can get complicated. Have a look at this article: A PerlIO layer with timeout. This explains how IO::Socket::Timeout works. I think this is what you seek.