AnyEvent
view release on metacpan or search on metacpan
lib/AnyEvent/Intro.pod view on Meta::CPAN
To avoid that, we refer to the watcher variable in the watcher callback.
This means that, when the C<tcp_connect> callback returns, perl thinks
(quite correctly) that the read watcher is still in use - namely inside
the inner callback - and thus keeps it alive even if nothing else in the
program refers to it anymore (it is much like Baron Münchhausen keeping
himself from dying by pulling himself out of a swamp).
The trick, however, is that instead of:
my $read_watcher = AnyEvent->io (...
The program does:
my $read_watcher; $read_watcher = AnyEvent->io (...
The reason for this is a quirk in the way Perl works: variable names
declared with C<my> are only visible in the I<next> statement. If the
whole C<< AnyEvent->io >> call, including the callback, would be done in
a single statement, the callback could not refer to the C<$read_watcher>
variable to C<undef>ine it, so it is done in two statements.
Whether you'd want to format it like this is of course a matter of style.
This way emphasizes that the declaration and assignment really are one
logical statement.
The callback itself calls C<sysread> for as many times as necessary, until
C<sysread> returns either an error or end-of-file:
cb => sub {
my $len = sysread $fh, $response, 1024, length $response;
if ($len <= 0) {
Note that C<sysread> has the ability to append data it reads to a scalar
if we specify an offset, a feature which we make use of in this example.
When C<sysread> indicates we are done, the callback C<undef>ines
the watcher and then C<send>s the response data to the condition
variable. All this has the following effects:
Undefining the watcher destroys it, as our callback was the only one still
having a reference to it. When the watcher gets destroyed, it destroys the
callback, which in turn means the C<$fh> handle is no longer used, so that
gets destroyed as well. The result is that all resources will be nicely
cleaned up by perl for us.
=head3 Using the finger client
Now, we could probably write the same finger client in a simpler way if
we used C<IO::Socket::INET>, ignored the problem of multiple hosts and
ignored IPv6 and a few other things that C<tcp_connect> handles for us.
But the main advantage is that we can not only run this finger function in
the background, we even can run multiple sessions in parallel, like this:
my $f1 = finger "kuriyama", "freebsd.org";
my $f2 = finger "icculus?listarchives=1", "icculus.org";
my $f3 = finger "mikachu", "icculus.org";
print "kuriyama's gpg key\n" , $f1->recv, "\n";
print "icculus' plan archive\n" , $f2->recv, "\n";
print "mikachu's plan zomgn\n" , $f3->recv, "\n";
It doesn't look like it, but in fact all three requests run in
parallel. The code waits for the first finger request to finish first, but
that doesn't keep it from executing them parallel: when the first C<recv>
call sees that the data isn't ready yet, it serves events for all three
requests automatically, until the first request has finished.
The second C<recv> call might either find the data is already there, or it
will continue handling events until that is the case, and so on.
By taking advantage of network latencies, which allows us to serve other
requests and events while we wait for an event on one socket, the overall
time to do these three requests will be greatly reduced, typically all
three are done in the same time as the slowest of the three requests.
By the way, you do not actually have to wait in the C<recv> method on an
AnyEvent condition variable - after all, waiting is evil - you can also
register a callback:
$f1->cb (sub {
my $response = shift->recv;
# ...
});
The callback will be invoked only when C<send> is called. In fact,
instead of returning a condition variable you could also pass a third
parameter to your finger function, the callback to invoke with the
response:
sub finger($$$) {
my ($user, $host, $cb) = @_;
How you implement it is a matter of taste - if you expect your function to
be used mainly in an event-based program you would normally prefer to pass
a callback directly. If you write a module and expect your users to use
it "synchronously" often (for example, a simple http-get script would not
really care much for events), then you would use a condition variable and
tell them "simply C<< ->recv >> the data".
=head3 Problems with the implementation and how to fix them
To make this example more real-world-ready, we would not only implement
some write buffering (for the paranoid, or maybe denial-of-service aware
security expert), but we would also have to handle timeouts and maybe
protocol errors.
Doing this quickly gets unwieldy, which is why we introduce
L<AnyEvent::Handle> in the next section, which takes care of all these
details for you and lets you concentrate on the actual protocol.
=head2 Implementing simple HTTP and HTTPS GET requests with AnyEvent::Handle
The L<AnyEvent::Handle> module has been hyped quite a bit in this document
so far, so let's see what it really offers.
As finger is such a simple protocol, let's try something slightly more
complicated: HTTP/1.0.
( run in 0.347 second using v1.01-cache-2.11-cpan-e1769b4cff6 )