关闭连接到 Mojo websocket 的 Mojo::IOLoop 重复事件

Posted

技术标签:

【中文标题】关闭连接到 Mojo websocket 的 Mojo::IOLoop 重复事件【英文标题】:Shutting down a Mojo::IOLoop recurring event connected to a Mojo websocket 【发布时间】:2016-08-08 23:17:33 【问题描述】:

我在玩 Mojolicious 和 websockets。我想将服务器上多个外部命令的输出发送到网页。我在连接和接收消息方面没有问题,但我也想向服务器发送一条消息以停止外部命令,同时让其他人继续向客户端发送消息。我也想在外部命令退出后停止检查。

外部命令只是一个每隔几秒输出一个整数的单行命令。我有两个 websocket,分别在divs 中显示数字。单击任何一个停止按钮都会发送消息,但这就是我需要弄清楚如何关闭该 websocket(并且仅该 websocket)并关闭外部命令的地方。

当我连接 websocket 时,我运行外部命令并设置 Mojo::IOLoop->recurring 以检查是否有输出。

当我想停止时,我认为我应该调用 Mojo::IOLoop->remove($id),但这似乎并没有完全删除它,并且我收到了类似 Mojo::Reactor::Poll: Timer failed: Can't call method "is_websocket" on an undefined value 的错误消息。

如果我在控制器对象上调用finish 来关闭 websocket,它似乎会停止一切。

我有整个Mojolicious::Lite app as a gist,但这是我所在的部分

use feature qw(signatures);
no warnings qw(experimental::signatures);
## other boilerplate redacted

websocket '/find' => sub ( $c ) 
    state $loop = Mojo::IOLoop->singleton;

    app->log->debug( "websocket for find" );
    $c->inactivity_timeout( 50 );

    my $id;
    $c->on( message => sub ( $ws, $message ) 
        my $json = decode_json( $message );
        my $command = $json->c;
        my $name    = $json->n;

        app->log->debug( "Got $command command for $name" );
        if( $command eq "start" ) 
            $id = run_command( $ws );
            app->log->debug( "run_command for $name returned [$id]" );
            
        elsif( $command eq "stop" ) 
            app->log->debug( "stopping loop for $name [$id]" );
            # XXX What should I do here?
            # $ws->finish;
            # $loop->remove( $id );
            
        elsif( $command eq "open" ) 
            app->log->debug( "opening websocket for $name" );
            
        
        );
     $c->on(
        finish => sub ( $c, $code ) 
            app->log->debug("WebSocket closed with status $code");
            
        );
    ;

app->start;

sub run_command ( $ws ) 
    app->log->debug( "In run_command: $ws" );
    open my $fh, "$^X -le '\$|++; while(1)  print int rand(100); sleep 3 ' |";
    $fh->autoflush;

    my $id;
    $id = Mojo::IOLoop->recurring( 1 => sub ($loop) 
        my $m = <$fh>;
        unless( defined $m ) 
            app->log->debug( "Closing down recurring loop from the inside [$id]" );
            # XXX: what should I do here?
            close $fh;
            return;
            ;
        chomp $m;
        app->log->debug( "Input [$m] for [$id] from $fh" );
        $ws->send( encode_json(  'm' => $m  ) );
        );

    return $id;
    

可能会从这个答案中受益的其他问题:

Output command to socket without buffering using Mojo::IOLoop

【问题讨论】:

Mojolicious 是否启用 Perl6 样式的子参数声明? Perl v5.20 具有子程序签名作为实验功能:effectiveperlprogramming.com/2015/04/… 【参考方案1】:

我玩了一下这个。 Logioniz's answer 让我觉得我不应该自己轮询或处理文件句柄细节。我仍然不知道它挂在哪里。

相反,我使用Mojo::Reactor 的io 来设置要监控的文件句柄:

sub run_command ( $ws ) 
    my $pid = open my $fh, "$^X -le '\$|++; print \$\$; while(1)  print int rand(100); sleep 3 ' |";
    $fh->autoflush;

    my $reactor = Mojo::IOLoop->singleton->reactor->io(
        $fh => sub ($reactor, $writeable) 
            my $m = <$fh>;
            chomp $m;
            $ws->send( encode_json(  'm' => $m  ) );
            
        );

    return ( $fh, $pid );
    

完成该命令后,我可以取消监视该文件句柄并终止该进程。我完成了 websocket:

    elsif( $command eq "stop" ) 
        $loop->reactor->watch( $fh, 0, 0 );
        kill 'KILL', $pid or app->log->debug( "Could not kill $pid: $!" );
        $ws->finish;
        

我仍然不知道为什么remove($fh) 不起作用。我想我这样做会泄露一些 IOLoop 的东西。

【讨论】:

【参考方案2】:

我认为你阻塞了事件循环,因为你的循环调用每秒和my $m = &lt;$fh&gt;; 等待结果大约 2-3 秒。所以你阻止事件循环。 我是这么认为的,因为当我运行您的应用程序时,事件finish 不会调用非活动超时,而是调用事件recurrentfinish 事件必须始终调用不活动超时。

我认为您的代码必须在单独的进程中以避免阻塞事件循环。

尝试使用this 模块在单独的进程中执行。 我write小例子。

【讨论】:

以上是关于关闭连接到 Mojo websocket 的 Mojo::IOLoop 重复事件的主要内容,如果未能解决你的问题,请参考以下文章

WebSocket 连接到 'ws://localhost:4444/subscriptions' 失败:WebSocket 在连接建立之前关闭

关闭连接后如何重新连接到websocket [重复]

WebSocket 连接到“wss://argus.chi.shopify.io”失败:WebSocket 在连接建立之前关闭

iOS 5 的 Websocket 连接立即关闭,我正在尝试连接到 node.js 服务器和 phonegap 应用程序

如何正确关闭 websocket

WebSocket 连接已关闭:代码 = 1006