… | |
… | |
21 | |
21 | |
22 | =cut |
22 | =cut |
23 | |
23 | |
24 | package AnyEvent::FastPing; |
24 | package AnyEvent::FastPing; |
25 | |
25 | |
26 | use strict; |
26 | use common::sense; |
27 | no warnings; |
|
|
28 | |
27 | |
29 | use AnyEvent; |
28 | use AnyEvent; |
30 | |
29 | |
31 | BEGIN { |
30 | BEGIN { |
32 | our $VERSION = '1.11'; |
31 | our $VERSION = '2.0'; |
33 | our @ISA = qw(Exporter); |
32 | our @ISA = qw(Exporter); |
34 | |
33 | |
35 | require Exporter; |
34 | require Exporter; |
36 | #Exporter::export_ok_tags (keys %EXPORT_TAGS); |
35 | #Exporter::export_ok_tags (keys %EXPORT_TAGS); |
37 | |
36 | |
38 | require XSLoader; |
37 | require XSLoader; |
39 | XSLoader::load (__PACKAGE__, $VERSION); |
38 | XSLoader::load (__PACKAGE__, $VERSION); |
40 | } |
39 | } |
41 | |
40 | |
42 | our ($THR_REQ_FD, $THR_RES_FD, $ICMP4_FD, $ICMP6_FD); |
41 | our ($THR_RES_FD, $ICMP4_FD, $ICMP6_FD); |
43 | |
42 | |
44 | our $THR_REQ_FH; open $THR_REQ_FH, ">&=$THR_REQ_FD" or die "FATAL: cannot fdopen"; |
|
|
45 | our $THR_RES_FH; open $THR_RES_FH, "<&=$THR_RES_FD" or die "FATAL: cannot fdopen"; |
43 | our $THR_RES_FH; open $THR_RES_FH, "<&=$THR_RES_FD" or die "FATAL: cannot fdopen"; |
46 | |
44 | |
47 | our $THR_REQ_W; |
45 | our $ICMP4_FH; our $ICMP4_W = $ICMP4_FD >= 0 && (open $ICMP4_FH, "<&=$ICMP4_FD") && AE::io $ICMP4_FH, 0, \&_recv_icmp4; |
48 | our $THR_RES_W = AnyEvent->io (fh => $THR_RES_FH, poll => 'r', cb => sub { |
46 | our $ICMP6_FH; our $ICMP6_W = $ICMP6_FD >= 0 && (open $ICMP6_FH, "<&=$ICMP6_FD") && AE::io $ICMP6_FH, 0, \&_recv_icmp6; |
49 | my $sv = _read_res |
|
|
50 | or return; |
|
|
51 | |
47 | |
52 | $sv->(); |
48 | =item AnyEvent::FastPing::ipv4_supported |
|
|
49 | |
|
|
50 | Returns true if IPv4 is supported in this module and on this system. |
|
|
51 | |
|
|
52 | =item AnyEvent::FastPing::ipv6_supported |
|
|
53 | |
|
|
54 | Returns true if IPv6 is supported in this module and on this system. |
|
|
55 | |
|
|
56 | =item AnyEvent::FastPing::icmp4_pktsize |
|
|
57 | |
|
|
58 | Returns the number of bytes each IPv4 ping packet has. |
|
|
59 | |
|
|
60 | =item AnyEvent::FastPing::icmp6_pktsize |
|
|
61 | |
|
|
62 | Returns the number of bytes each IPv4 ping packet has. |
|
|
63 | |
|
|
64 | =cut |
|
|
65 | |
|
|
66 | sub new { |
|
|
67 | my ($klass) = @_; |
|
|
68 | |
|
|
69 | _new $klass, (rand 65536), (rand 65536), (rand 65536) |
|
|
70 | } |
|
|
71 | |
|
|
72 | our @IDLE_CB; |
|
|
73 | |
|
|
74 | sub DESTROY { |
|
|
75 | undef $IDLE_CB[ &id ]; |
|
|
76 | &_free; |
|
|
77 | } |
|
|
78 | |
|
|
79 | sub on_idle { |
|
|
80 | $IDLE_CB[ &id ] = $_[1]; |
|
|
81 | } |
|
|
82 | |
|
|
83 | our $THR_RES_W = AE::io $THR_RES_FH, 0, sub { |
|
|
84 | sysread $THR_RES_FH, my $buf, 8; |
|
|
85 | |
|
|
86 | for my $id (unpack "S*", $buf) { |
|
|
87 | _stop_id $id; |
|
|
88 | ($IDLE_CB[$id] || sub { })->(); |
|
|
89 | } |
|
|
90 | }; |
|
|
91 | |
|
|
92 | for(1..10) { |
|
|
93 | my $p = new AnyEvent::FastPing;#d# |
|
|
94 | $p->interval (0); |
|
|
95 | $p->max_rtt (0.5); |
|
|
96 | #$p->add_range (v127.0.0.1, v127.255.255.254, 0); |
|
|
97 | $p->add_range (v1.0.0.1, v1.255.255.254, 0); |
|
|
98 | $p->on_idle (my $cv = AE::cv); |
|
|
99 | my $cnt; |
|
|
100 | $p->on_recv (sub { |
|
|
101 | $cnt++; |
53 | }); |
102 | }); |
54 | |
103 | $p->start; |
55 | our $THR_REQ_BUF; |
104 | $cv->recv; |
56 | |
105 | warn $cnt; |
57 | sub _send_req($) { |
|
|
58 | $THR_REQ_BUF .= $_[0]; |
|
|
59 | |
|
|
60 | $THR_REQ_W ||= AnyEvent->io (fh => $THR_REQ_FH, poll => 'w', cb => sub { |
|
|
61 | my $len = syswrite $THR_REQ_FH, $THR_REQ_BUF; |
|
|
62 | substr $THR_REQ_BUF, 0, $len, ""; |
|
|
63 | |
|
|
64 | undef $THR_REQ_W unless length $THR_REQ_BUF; |
|
|
65 | }); |
|
|
66 | } |
106 | } |
67 | |
|
|
68 | =item AnyEvent::FastPing::ipv4_supported |
|
|
69 | |
|
|
70 | Returns true if IPv4 is supported in this module and on this system. |
|
|
71 | |
|
|
72 | =item AnyEvent::FastPing::ipv6_supported |
|
|
73 | |
|
|
74 | Returns true if IPv6 is supported in this module and on this system. |
|
|
75 | |
|
|
76 | =item AnyEvent::FastPing::icmp4_pktsize |
|
|
77 | |
|
|
78 | Returns the number of bytes each IPv4 ping packet has. |
|
|
79 | |
|
|
80 | =item AnyEvent::FastPing::icmp6_pktsize |
|
|
81 | |
|
|
82 | Returns the number of bytes each IPv4 ping packet has. |
|
|
83 | |
107 | |
84 | =item AnyEvent::FastPing::icmp_ping [ranges...], $send_interval, $payload, \&callback |
108 | =item AnyEvent::FastPing::icmp_ping [ranges...], $send_interval, $payload, \&callback |
85 | |
109 | |
86 | Ping the given IPv4 address ranges. Each range is an arrayref of the |
110 | Ping the given IPv4 address ranges. Each range is an arrayref of the |
87 | form C<[lo, hi, interval]>, where C<lo> and C<hi> are octet strings with |
111 | form C<[lo, hi, interval]>, where C<lo> and C<hi> are octet strings with |
… | |
… | |
141 | $done->wait; |
165 | $done->wait; |
142 | |
166 | |
143 | =cut |
167 | =cut |
144 | |
168 | |
145 | sub icmp_ping($$$&) { |
169 | sub icmp_ping($$$&) { |
146 | _send_req _req_icmp_ping @_; |
170 | # _send_req _req_icmp_ping @_; |
147 | } |
171 | } |
148 | |
|
|
149 | our $ICMP4_FH; |
|
|
150 | our $ICMP4_W = (open $ICMP4_FH, "<&=$ICMP4_FD") && AnyEvent->io (fh => $ICMP4_FH, poll => 'r', cb => \&_recv_icmp4); |
|
|
151 | our $ICMP6_FH; |
|
|
152 | our $ICMP6_W = (open $ICMP6_FH, "<&=$ICMP6_FD") && AnyEvent->io (fh => $ICMP6_FH, poll => 'r', cb => \&_recv_icmp6); |
|
|
153 | |
172 | |
154 | =item AnyEvent::FastPing::register_cb \&cb |
173 | =item AnyEvent::FastPing::register_cb \&cb |
155 | |
174 | |
156 | Register a callback that is called for every received ping reply |
175 | Register a callback that is called for every received ping reply |
157 | (regardless of whether a ping is still in process or not and regardless of |
176 | (regardless of whether a ping is still in process or not and regardless of |
158 | whether the reply is actually a reply to a ping sent earlier). |
177 | whether the reply is actually a reply to a ping sent earlier). |
159 | |
178 | |
160 | The code reference gets a single parameter - an arrayref with an |
179 | The code reference gets a single parameter - an arrayref with an |
161 | entry for each received packet (replies are beign batched for greater |
180 | entry for each received packet (replies are being batched for greater |
162 | efficiency). Each packet is represented by an arrayref with three members: |
181 | efficiency). Each packet is represented by an arrayref with three members: |
163 | the source address (an octet string of either 4 (IPv4) or 16 (IPv6) octets |
182 | the source address (an octet string of either 4 (IPv4) or 16 (IPv6) octets |
164 | length), the payload as passed to C<icmp_ping> and the round trip time in |
183 | length), the payload as passed to C<icmp_ping> and the round trip time in |
165 | seconds. |
184 | seconds. |
|
|
185 | |
|
|
186 | Example: register a callback which simply dumps the received data. Since |
|
|
187 | the coderef is created on the fly via sub, it would be hard to unregister |
|
|
188 | this callback again :) |
|
|
189 | |
|
|
190 | AnyEvent::FastPing::register_cb sub { |
|
|
191 | for (@{$_[0]}) { |
|
|
192 | printf "%s %d %g\n", |
|
|
193 | (4 == length $_->[0] ? inet_ntoa $_->[0] : Socket6::inet_ntop (&AF_INET6, $_->[0])), |
|
|
194 | $_->[2], |
|
|
195 | $_->[1]; |
|
|
196 | } |
|
|
197 | }; |
166 | |
198 | |
167 | Example: a single ping reply with payload of 1 from C<::1> gets passed |
199 | Example: a single ping reply with payload of 1 from C<::1> gets passed |
168 | like this: |
200 | like this: |
169 | |
201 | |
170 | [ [ |
202 | [ [ |
… | |
… | |
196 | |
228 | |
197 | =cut |
229 | =cut |
198 | |
230 | |
199 | our @CB; |
231 | our @CB; |
200 | |
232 | |
201 | sub register_cb(&) { |
233 | sub register_cb($) { |
202 | push @CB, $_[0]; |
234 | push @CB, $_[0]; |
203 | } |
235 | } |
204 | |
236 | |
205 | sub unregister_cb($) { |
237 | sub unregister_cb($) { |
206 | @CB = grep $_ != $_[0], @CB; |
238 | @CB = grep $_ != $_[0], @CB; |