I am using the following code. I want to timeout and close the connection after 20 seconds, tried with alarms but nothing worked. Here is my code:
my $socket_resp = IO::Socket::INET->new(Blocking => 0, LocalPort => $comm_port, Proto => 'udp', Timeout => 2);
$socket_resp->setsockopt(SO_RCVTIMEO, SO_RCVTIMEO, 10);
print "Waiting for Response On Port $comm_port\n";
while (my $recieved_data = $socket_resp->getline()) {
chomp($recieved_data);
print "$recieved_data\n";
if ($recieved_data =~ m/^done/i) {
last;
}
}
$socket_resp->close();
Wrapping your entire read loop in an alarm, as suggested in the other question will very likely do what you want. You don't show us code, so we don't know why your previous attempts failed.
That said, SO_RCVTIMEO can be made to work, too, albeit a bit differently.
You want a blocking rather than non-blocking socket in this case. You also want to setsockopt correctly, which requires SOL_SOCKET and pack()ing a struct timeval:
my $s = IO::Socket::INET->new(Proto => 'udp', ...); # N.B.: blocking
$s->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 20, 0)); # N.B.: pack()
while (<$s>) {
...
}
Now, the above waits 20 seconds for each underlying call to read(), which may be more than the number of lines returned to your application. That is, if I send your application "foo\n" in one datagram and then nothing, you'll timeout after 20 seconds. However, I might send "f", then wait 19 seconds, "o", then wait 19 seconds, "o", then wait 19 seconds, ... you get the idea.)
Related
I have a simple server socket
my $server = IO::Socket::INET->new(
LocalPort => $config{'local-port'},
Reuse => 1,
Listen => 10,
Timeout => 1,
Blocking => 1
) or croak "Couln't start server: $!\n";
I want to keep doing some routine while client is connected
while (1) {
my $client = $server->accept;
next unless $client;
say "new connection";
# this loop never ends
while ( $client->connected ) {
# do_work();
}
say "connection closed";
}
But $socket->connected always return true (or seemingly until tcp keepalive is not sent).
Is there a way to check if client is still connected (for example if it didn't send anything in 10 seconds it can be considered disconnected)?
Is there a way to check if client is still connected (for example if it didn't send anything in 10 seconds it can be considered disconnected)?
"Disconnected" in terms of TCP means that an explicit TCP shutdown was done. No data for 10 seconds is different from this - it is an expectation that the application will send data every 10 seconds and if this does not happen something is wrong, like crash of peer or loss of connectivity.
To detect any of this it is necessary to actually attempt to read from the socket. In case of an explicit TCP disconnect the read will return that 0 bytes are read w/o any failure.
This is different from having the connection open but no data available - here the read will hang on a blocking socket so select or similar should be used to check first if there would be something to read. Alternatively use a non-blocking socket in which case the read would fail with EWOULDBLOCK (or EAGAIN, which is the same on most OS).
If actually reading the data does not fit in the way the program is designed, one could instead also use a combination of select (check that something is read) and MSG_PEEK to just check the state of the socket buffer and the underlying connection, i.e. something like this:
vec(my $rin = '', fileno($socket), 1) = 1;
my $n = select($rin, undef, undef, undef, 0);
if ($n<0) {
# something wrong, socket likely already explicitly locally closed
} elsif ($n == 0) {
# read would block, i.e. no explicit disconnect and no data to read
} else {
$n = recv($socket, my $buf, 1, MSG_PEEK);
if ($n>0) {
# data available in socket buffer
} elsif ($n==0) {
# no data available and no errno -> socket closed by peer
} else {
# socket broken, for example keep alive timer failed
}
}
I'm trying to write a small service that responds to a couple commands (to check/report on status), but every 15 seconds or so breaks out of its polling status to check a database for messages and fork off a child to do some processing.
I'm sub-classing Net::Server::HTTP, for example:
my $service = MyService->new("port" => 8080);
$service->run(app => {
"/ping" => sub { print_client("Pong"); },
"/status" => sub { print_client("Good, thanks"); },
});
but I can't figure out how to get a timeout through the class hierarchy to reach the socket's select call. I was hoping I could pass a timeout through, or use a while(1) with an alarm to break out of the run method, but neither is working.
What I was /hoping/ for was to do:
while (1) {
$service->run(
timeout => 15,
app => { ... }
});
check_database();
}
Is there a better way to do this while keeping the code simple?
I'm trying to build a timeout scenario in my Catalyst, AnyEvent, Websocket app.
For that I'm using
AnyEvent->timer
which should be called after let's say a few seconds of inactivity (no more WS frames coming in).
The problem is, that my timer is never executed:
my $w = AnyEvent->timer (after => 3,
cb => sub {
warn "TIMEOUT!";
});
$self->{server} = Protocol::WebSocket::Handshake::Server->new_from_psgi(
$c->req->env) or die $c->log->fatal($!);
$self->{handle} = AnyEvent::Handle->new(
fh => $c->req->io_fh,
on_error => sub {
my ($hd, $fatal, $msg) = #_;
$clean_up->();
}
);
die $c->log->fatal("WS Server error: '$_'")
if $self->{server}->error;
$self->{server}->parse($self->{handle}->fh);
$self->{handle}->push_write($self->{server}->to_string);
$self->{handle}->on_read(sub {
(my $frame = $self->{server}->build_frame)->append($_[0]->rbuf);
while (my $frame_msg = $frame->next) {
...
}
The timer callback is never executed.
My guess would be, that the timer doesn't work inside another Event loop (AnyEvent::Handle)?
Are you actually getting into the event loop for the timer to be processed? Your code snippet does not indicate this.
Also, AnyEvent::Handle has inactivity timeouts built-in:
timeout => $fractional_seconds
If non-zero, then this enables an "inactivity" timeout: whenever
this many seconds pass without a successful read or write on the
underlying file handle, the "on_timeout" callback will be invoked
(and if that one is missing, a non-fatal "ETIMEDOUT" error will
be raised).
Note that timeout processing is also active when you currently do
not have any outstanding read or write requests: If you plan to
keep the connection idle then you should disable the timout
temporarily or ignore the timeout in the "on_timeout" callback,
in which case AnyEvent::Handle will simply restart the timeout.
Zero (the default) disables this timeout.
on_timeout => $cb->($handle)
Called whenever the inactivity timeout passes. If you return from
this callback, then the timeout will be reset as if some activity
had happened, so this condition is not fatal in any way.
I need to make blocking socket read end by timeout. I read this question, I learned that IO::Socket::INET doesn't pay attention to Timeout option and learned about solution using eval/alarm. But I'm working on Windows and alarm doesn't work properly. Is there any other solution?
Prior to reading from the socket, use the 4-argument version of select, with the desired timeout, to test whether any data is available on the socket to be read.
Also see the IO::Select module, and specifically the IO::Select::can_read($timeout) method to test if a socket read will block or not.
Example:
$read_timeout = 5.0; # seconds
$socket = IO::Socket->new( ... ); # socket to read from
$selector = IO::Select->new;
$selector->add( $socket );
...
#ready = $selector->can_read( $read_timeout );
if (#ready > 0) {
$socket->read( $buffer, 128 ); # copy 128 bytes into $buffer
} else {
warn "data not available on socket now";
}
I have a basic perl HTTP server using HTTP::Daemon. When I stop and start the script, it appears that the port is still being listened on and I get an error message saying that my HTTP::Daemon instance is undefined. If I try to start the script about a minute after it has stopped, it works fine and can bind to the port again.
Is there any way to stop listening on the port when the program terminates instead of having to wait for it to timeout?
use HTTP::Daemon;
use HTTP::Status;
my $d = new HTTP::Daemon(LocalAddr => 'localhost', LocalPort => 8000);
while (my $c = $d->accept) {
while (my $r = $c->get_request) {
$c->send_error(RC_FORBIDDEN)
}
$c->close;
undef($c);
}
EDIT:
As per DVK's response, I tried calling $d->close() but am still getting the same error when trying to restart my script.
END { $d->close(); }
$SIG{'INT'} = 'CLEANUP';
$SIG{__WARN__} = 'CLEANUP';
$SIG{__DIE__} = 'CLEANUP';
sub CLEANUP {
$d->close();
undef($d);
print "\n\nCaught Interrupt (^C), Aborting\n";
exit(1);
}
I found a solution to my problem by setting ReuseAddr => 1 when creating the HTTP::Daemon.
my $d = new HTTP::Daemon(
ReuseAddr => 1,
LocalAddr => 'localhost',
LocalPort => 8000);
Did you try $d->close() at the end of the program?
If not, try that. It's not documented in HTTP::Daemon POD example but the method should be available (inherited from IO::Socket)
Remember that you might need to be creative about where to call it, e.g. it might need to go into __DIE__ handler or END {} block