Perhaps AnyEvent::FastPing can be of help. Alternatively ...
Being unfamiliar, as well, with AnyEvent I could be wrong but how about using a global table (shared among the timer callbacks - is this allowed? It seems OK.) to store the state of each host like: my $state{'192.168.100.1' => [0, ['success at timexxx with packetsxxx', 'failed at ...', ...]]}; where the first array element is a flag indicating whether a ping is in progress right now or not. And the second element stores the history of ping results, possibly in a finite-size data structure (Queue?).
With the above idea and modifying slightly hippo's code:
use strict; use warnings; use AnyEvent; use AnyEvent::Ping; use Data::Dumper; # separate the hosts wrt their timer groups my @todoList1 = map { "192.168.100." . $_ } (1 .. 2); my @todoList2 = map { "192.168.100." . $_ } (3 .. 4); # a global hash keyed on IPs, => [0,[]]: no pinging now, =>[1,[]]: pin +g in progress # the array ref storing ping history can be a Queue of finite size my $pingState = { map { $_ => [0,[]] } @todoList1, @todoList2 }; my $cv = AnyEvent->condvar; my @timers = ( AnyEvent->timer ( after => 2, interval => 5, cb => sub { warn "Timer 1 at " . AnyEvent->now . "\n"; ping_hosts (1, @todoList1); } ), AnyEvent->timer ( after => 2, interval => 7, cb => sub { warn "Timer 2 at " . AnyEvent->now . "\n"; ping_hosts (2, @todoList2); } ), AnyEvent->timer ( after => 20, cb => sub { warn "Timer 3 at " . AnyEvent->now . "\n"; $cv->send; } ), ); $cv->recv; print "DONE.\n".Dumper($pingState); sub ping_hosts { my ($num, @hosts) = @_; my $ping_timeout = 2; #seconds to wait for response my $ping_attempts = 4; #number of pings to issue, my $ping_pktsize = 32; #packet size of ping my $ping = AnyEvent::Ping->new (packet_size => $ping_pktsize); $ping->timeout ($ping_timeout); #$cv_ping->begin(sub { shift->send($_[0]) }); foreach my $host (@hosts) { if( $pingState->{$host}->[0] == 1 ){ print "ping in progress already to host '$host', skipp +ing till the next timer ...\n"; next } $pingState->{$host}->[0] = 1; # set the "ping in progress" fla +g $ping->ping ( #mock_ping( # emulate the ping $host, $ping_attempts, sub { print "Pinger $num: $host state:" . Dumper ($_[0]) . " +\n"; # store the ping results in global history and remove +the "ping-in-progress" flag push @{$pingState->{$host}->[1]}, $_[0]; $pingState->{$host}->[0] = 0; # ping finished }, # mock_ping extra parameters # $ping_timeout, # 50/100 # probability of a failed ping ); } } # code below is # optional for testing without ping'ing sub mock_ping { my ($host, $attempts, $cb, $timeout, $failure_prob) = @_; # no $attempts, just fail or succeed so many % of times my $started = AnyEvent->time; if( rand()<$failure_prob ){ # simulate a failed ping with lots of delay # so as to coincide with next timer event AEsleep($timeout*3); $cb->([$host, "$started -> fail -> ".AnyEvent->time]); return 0; } else { # a successful ping safely within the timeout AEsleep($timeout/2); # simulate a ping $cb->([$host, "$started -> succ -> ".AnyEvent->time]); return 1; # success ping } } # this is the closest I came to emulating spawning a blocking # ping. Note that using Perl's sleep() is no-go with AnyEvent's timers # AFAIU, re: "It is usually a mistake to intermix alarm and sleep call +s" # and sadly there is no AE::sleep() AFAIK sub AEsleep($) { `sleep $_[0]` }
btw, in my Linux the pinging code needs to be run as root.
bw, bliako
In reply to Re: Implementing AnyEvent::Ping multiple times
by bliako
in thread Implementing AnyEvent::Ping multiple times
by mmoorreett
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |