Creating A Single Threaded Server with AnyEvent (Perl)

你说的曾经没有我的故事 提交于 2019-12-07 09:28:31

问题


I'm working on creating a local service to listen on localhost and provide a basic call and response type interface. What I'd like to start with is a baby server that you can connect to over telnet and echoes what it receives.

I've heard AnyEvent is great for this, but the documentation for AnyEvent::Socket does not give a very good example how to do this. I'd like to build this with AnyEvent, AnyEvent::Socket and AnyEvent::Handle.

Right now the little server code looks like this:

#!/usr/bin/env perl

use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;

my $cv = AnyEvent->condvar;

my $host = '127.0.0.1';
my $port = 44244;

tcp_server($host, $port, sub {
    my($fh) = @_;

    my $cv = AnyEvent->condvar;

    my $handle;
    $handle = AnyEvent::Handle->new(
        fh => $fh,
        poll => "r",
        on_read => sub {
             my($self) = @_;
             print "Received: " . $self->rbuf . "\n";
             $cv->send;
        }
    );

    $cv->recv;
});

print "Listening on $host\n";

$cv->wait;

This doesn't work and also if I telnet to localhost:44244 I get this:

EV: error in callback (ignoring): AnyEvent::CondVar: 
recursive blocking wait attempted at server.pl line 29.

I think if I understand how to make a small single threaded server that I can connect to over telnet and prints out whatever its given and then waits for more input, I could take it a lot further from there. Any ideas?


回答1:


You're blocking inside a callback. That's not allowed. There are a few ways to handle this. My preference is to launch a Coro thread from within the tcp_server callback. But without Coro, something like this might be what you're looking for:

#!/usr/bin/env perl5.16.2

use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;

my $cv = AE::cv;

my $host = '127.0.0.1';
my $port = 44244;

my %connections;
tcp_server(
           $host, $port, sub {
               my ($fh) = @_;

               print "Connected...\n";

               my $handle;
               $handle = AnyEvent::Handle->new(
                                               fh => $fh,
                                               poll => 'r',
                                               on_read => sub {
                                                   my ($self) = @_;
                                                   print "Received: " . $self->rbuf . "\n";
                                               },
                                               on_eof => sub {
                                                   my ($hdl) = @_;
                                                   $hdl->destroy();
                                               },
                                              );
               $connections{$handle} = $handle; # keep it alive.

               return;
           });

print "Listening on $host\n";

$cv->recv;

Note that I'm only waiting on one condvar. And I'm storing the handles to keep the AnyEvent::Handle objects alive longer. Work to clean up the $self->rbuf is left as an excersise for the reader :-)

Question cross-posted, answer, too :-)




回答2:


I have heard good things about AnyEvent as well, but have not used it. I wrote a small nonblocking server in the past using IO::Select. There is an example in the documentation for that module (I've added a few lines):

use IO::Select;
use IO::Socket;

$lsn = new IO::Socket::INET(Listen => 1, LocalPort => 8080);
$sel = new IO::Select( $lsn );

while(@ready = $sel->can_read) {
    foreach $fh (@ready) {
        if($fh == $lsn) {
            # Create a new socket
            $new = $lsn->accept;
            $sel->add($new);
        }
        else {
            # Process socket
            my $input = <$fh>;
            print $fh "Hello there. You said: $input\n";

            # Maybe we have finished with the socket
            $sel->remove($fh);
            $fh->close;
        }
    }
}



回答3:


I'm not sure what your condvar is trying to trigger there. Use it to send state, like:

#!/usr/bin/env perl

use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;

my $host = '127.0.0.1';
my $port = 44244;

my $exit = AnyEvent->condvar;

tcp_server($host, $port, sub {
    my($fh) = @_;

    my $handle; $handle = AnyEvent::Handle->new(
        fh => $fh,
        poll => "r",
        on_read => sub {
             my($self) = @_;
             print "Received: " . $self->rbuf . "\n";
             if ($self->rbuf eq 'exit') {
               $exit->send;
             }
        }
    );

});

print "Listening on $host\n";

$exit->recv;


来源:https://stackoverflow.com/questions/13260802/creating-a-single-threaded-server-with-anyevent-perl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!