Perl 的 AnyEvent::IRC::Client 库在发送消息后不关闭

Posted

技术标签:

【中文标题】Perl 的 AnyEvent::IRC::Client 库在发送消息后不关闭【英文标题】:Perl's AnyEvent::IRC::Client library does not close after sending messages 【发布时间】:2021-01-09 00:15:51 【问题描述】:

我正在探索 AnyEvent::IRC::Client 库以将消息列表发送到 IRC 频道。我遵循其 metacpan 站点(参考:https://metacpan.org/pod/AnyEvent::IRC::Client)中的示例代码,除了我需要发送消息列表而不是单个消息。因此,我能够成功地将数组中的所有消息发送到 IRC 频道。问题是事件循环没有在最后关闭(即我必须按 Ctrl+C 来终止程序)。因此,任何有关如何关闭事件循环的见解都将不胜感激。谢谢!

# Send some messages to IRC channel
use strict ;
use warnings 'all' ;
no warnings 'uninitialized' ;
use AnyEvent ;
use AnyEvent::IRC::Client ;
use Data::Dumper ;
sub say  print @_, "\n" 

my @messages = ( 'msg1','msg2','msg3','msg4','msg5','msg6','msg7','msg8','msg9','msg10' ) ;

sendIrc( 'ircServer', 6667, '#ircChannel1', 'user123', 'psswd', \@messages ) ;

sub sendIrc #Params: ircServer(Str), port(Int), channel(Str), nick(Str), psswd(Str), messages(ArrRef<Str>) ; #Return: void ;
  my ( $server, $port, $chan, $nick, $psswd, $messages ) = @_ ;
  my $timer ;
  my $condVar = AnyEvent->condvar ;
  my $con = AnyEvent::IRC::Client->new ;
  
  $con->reg_cb( connect => sub 
    my ( $cond, $err ) = @_ ;
    if ( defined $err ) 
      warn "connect error: $err\n" ;
      return ;
    #end if
  ) ;

  $con->reg_cb( registered => sub 
    say "User is in!" ;
  ) ;

  $con->reg_cb( disconnect => sub 
    say "User is out!" ;
  ) ;

  $con->reg_cb( sent => sub 
    my ( $con ) = @_ ;
    if ( $_[2] eq 'PRIVMSG' ) 
      say "Msg sent!" ;
      $timer = AnyEvent->timer (
        after => 1 ,
        cb    => sub 
          undef $timer ;
          $condVar->end ;
        #end callback
      );#end timer
    #end if
  ) ;

  $con->connect( $server, $port,  nick => $nick, password => $psswd  ) ;
  for my $msg ( @$messages ) 
    $condVar->begin ;
    $con->send_srv( PRIVMSG => ( $chan, $msg ) ) ;
  #end for
  $condVar->wait ;
  $con->disconnect ;
  return ;
#end sub

【问题讨论】:

您重用了$timer,因此它只会被调用一次,并且您会得到不平衡的开始/结束调用。您可以在回调中重置计时器并确保立即致电-&gt;end。或者更容易从回调中删除计时器并在断开连接之前使用计时器。 嗨@clamp,为了澄清,我只需要删除“my $timer;”主声明行并使用单独的计时器实例,即。 “$con->reg_cb(sent => sub....”块内的“my $timer = AnyEvent->timer(...)”,应该是这样吧? 不,这将导致所有计时器同时运行。不幸的是sent 触发得太早了,你必须给它一秒钟才能真正完成。您需要检查计时器是否仍在运行(已定义),如果是,则使用累积时间重新安排。这就是为什么我认为在断开连接之前设置一个更长的计时器会更容易。 因此,如果使用单计时器方法,则提供给“after”属性的秒数必须与数组中的消息数成正比,即。如果有 10 条消息,那么它将是:“after => 10 ;如果有 20 条消息,它将是“after => 20”,对吗? 在我的环境中似乎是这样。消息排队,每条消息大约需要 1 秒。 【参考方案1】:

问题是,AnyEvent::IRC::Client 将消息排入队列并立即发出 sent 事件。当消息实际发送到服务器时,似乎没有触发事件。每条消息的处理也需要一定的时间。我修改了您的代码以跟踪活动作业的数量并相应地重置计时器。这不是一个稳健的解决方案。如果每个作业的估计时间发生变化,预计这会失败。

use strict ;
use warnings 'all' ;
no warnings 'uninitialized' ;
use AnyEvent ;
use AnyEvent::IRC::Client ;
use Data::Dumper ;
sub say  print @_, "\n" 
my @messages = map "msg$_" 0..10 ;

sendIrc( 'ircServer', 6667, '#ircChannel1', 'user123', 'psswd', \@messages ) ;

sub sendIrc #Params: ircServer(Str), port(Int), channel(Str), nick(Str), psswd(Str), messages(ArrRef<Str>) ; #Return: void ;
  my ( $server, $port, $chan, $nick, $psswd, $messages ) = @_ ;
  my $timer;
  my $jobs = 0;# count of jobs
  my $time_per_job = 1;# seconds
  my $condVar = AnyEvent->condvar ;
  my $con = AnyEvent::IRC::Client->new ;
  my $cb = sub shift->send("done\n");
  $condVar->begin($cb); # one begin/end pair to wrap everything

  $con->reg_cb( connect => sub 
    my ( $cond, $err ) = @_ ;
    if ( defined $err ) 
      warn "connect error: $err\n" ;
      return ;
    #end if
  ) ;

  $con->reg_cb( registered => sub 
                    say "User is in!" ;
  ) ;

  $con->reg_cb( disconnect => sub 
    say "User is out!" ;
  ) ;

  $con->reg_cb( sent => sub 
    if ( $_[2] eq 'PRIVMSG' ) 
        $jobs++;
        print "jobs waiting: $jobs\n";
        $timer = AnyEvent->timer (
            after => $jobs * $time_per_job ,
            cb    => sub while ($jobs) $condVar->end;
                                         $jobs--;
                      ,#end callback
         );#end timer
    #end if
  ) ;

  $con->connect( $server, $port,  nick => $nick, password => $psswd  ) ;

  for my $msg ( @$messages ) 
      $con->send_srv( PRIVMSG => ( $chan, $msg ) ) ;
      $condVar->begin;
  #end for
  
  $condVar->end;
  my $isDone = $condVar->recv;
  print $isDone;
  $con->disconnect ;
  return ;
#end sub

【讨论】:

感谢@clamp 的所有帮助!

以上是关于Perl 的 AnyEvent::IRC::Client 库在发送消息后不关闭的主要内容,如果未能解决你的问题,请参考以下文章

Perl 之父同意 Perl 6 改名为 Raku

Perl 的 rpm 版本不同于“perl -v”

Perl模块推荐23——Perl::Shell

Perl基础速成

Perl语言入门

perl第一弹-perl的特点