【问题标题】:Creating A Single Threaded Server with AnyEvent (Perl)使用 AnyEvent (Perl) 创建单线程服务器
【发布时间】:2012-11-06 22:59:06
【问题描述】:

我正在创建一个本地服务来监听 localhost 并提供一个基本的调用和响应类型接口。我想开始的是一个婴儿服务器,您可以通过 telnet 连接到它并回显它接收到的内容。

我听说 AnyEvent 对此非常有用,但 AnyEvent::Socket 的文档并没有给出如何做到这一点的很好的例子。我想用 AnyEvent、AnyEvent::Socket 和 AnyEvent::Handle 来构建它。

现在小服务器代码如下所示:

#!/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;

这不起作用,如果我 telnet 到 localhost:44244 我得到这个:

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

我想如果我知道如何制作一个小型单线程服务器,我可以通过 telnet 连接到它并打印出它给定的任何内容,然后等待更多输入,我可以从那里走得更远。有什么想法吗?

【问题讨论】:

标签: perl webserver anyevent


【解决方案1】:

您在回调中阻塞。这是不允许的。有几种方法可以处理这个问题。我的偏好是从 tcp_server 回调中启动 Coro 线程。但如果没有 Coro,您可能正在寻找这样的东西:

#!/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;

请注意,我只在等待一个 condvar。我正在存储句柄以使 AnyEvent::Handle 对象保持更长时间。清理 $self->rbuf 的工作留给读者作为练习:-)

问题交叉发布,answer, too :-)

【讨论】:

  • 太棒了,谢谢 :-) 嘿问题,您如何将数据传送回客户端?打印到 $fh?
  • @DavidWilliams - 不,这会阻止。请参阅AnyEvent::Handle 文档,它在概要中:$hdl->push_write
【解决方案2】:

我也听说过关于 AnyEvent 的好消息,但没有使用它。我过去使用 IO::Select 编写了一个小型非阻塞服务器。 documentation for that module 中有一个例子(我已经添加了几行):

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】:

    我不确定你的 condvar 试图在那里触发什么。用它来发送状态,比如:

    #!/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;
    

    【讨论】:

    • 当我在 telnet 上尝试这个时,我得到这个:
      $ telnet localhost 44244 Trying 127.0.0.1... 连接到 localhost。转义字符是 '^]'。外部主机关闭连接。
      无论如何要让它阅读、打印然后再等待?
    猜你喜欢
    • 1970-01-01
    • 2019-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多