Mojolicious和延迟WebSocket

9

我有这段代码,想要每隔X秒向服务器推送一个字符串,如下:

#!/usr/bin/env perl
use Mojolicious::Lite;
use EV;
use AnyEvent;
use POSIX qw(strftime);

get '/' => sub {
    my $self = shift;

    $self->render('main');
};

websocket '/echo' => sub {
        my $self = shift;
        my $w;
        $w = AE::timer 3, 1, sub {
                    $self->send('Got it');
        };
      #  $self->send(strftime("Server $$: %a %b %e %H:%M:%S %Y", localtime));
};
app->start();

__DATA__
@@ main.html.ep
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
</head>
<body>
<table id="tableID">
<tbody>
</tbody>
</table>
<script type="text/javascript">
var ws = new WebSocket('ws://192.168.1.104:3000/echo');
      ws.onopen = function () {
        alert('Connection opened');
      };
      ws.onerror = function() { alert("Error"); };
      ws.onmessage = function (msg) {
          $("#tableID").find('tbody')
                       .append($('<tr>')
                       .append($('<td>')
                       .text(msg.data)
            )
        );

      };
</script>
</body>
</html>

据我所知,Mojo使用IO::Loop事件循环,我猜应该可以与AnyEvent兼容。但是目前这种方式不起作用,我想知道为什么。当我删除AE部分并取消注释简单的send时,我可以在浏览器中看到结果。附言:我只是在尝试WebSockets和Mojo,因为我将需要同时使用Mojo、AnyEvent和WebSockets来完成一些项目。

请问您能否同时包含模板? - Joel Berger
1个回答

7
问题似乎在于发送消息尝试在GET连接升级为WebSocket之前发生。我仍在努力追踪,与此同时,这个方法可以解决问题。
#!/usr/bin/env perl
use Mojolicious::Lite;
use EV;
use AnyEvent;
#use POSIX qw(strftime);

get '/' => sub {
    my $self = shift;

    $self->render('main');
};

websocket '/echo' => sub {
        my $self = shift;
        my $w;

        $self->on(finish => sub { Mojo::IOLoop->remove($w) });
      #  $self->send(strftime("Server $$: %a %b %e %H:%M:%S %Y", localtime));

        $w = Mojo::IOLoop->recurring( 1 => sub{
                    $self->send('Got it');
        });
};
app->start();

__DATA__
@@ main.html.ep
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
</head>
<body>
<table id="tableID">
<tbody>
</tbody>
</table>
<script type="text/javascript">
var ws = new WebSocket("<%= url_for('echo')->to_abs %>");
      ws.onopen = function () {
        alert('Connection opened');
      };
      ws.onerror = function() { alert("Error"); };
      ws.onmessage = function (msg) {
          $("#tableID").find('tbody')
                       .append($('<tr>')
                       .append($('<td>')
                       .text(msg.data)
            )
        );

      };
</script>
</body>
</html>

注意,在我的示例中,调试输出显示为101 switching protocols,而您的示例尝试自动呈现echo.html.ep
此外,我更改了模板以生成websocket url。
更新:
似乎为了连接到websocket,有必要订阅事件。在这个例子中,我订阅了finish事件,这也是您想要停止计时器的方式。
#!/usr/bin/env perl
use Mojolicious::Lite;
use EV;
use AnyEvent;
#use POSIX qw(strftime);

get '/' => sub {
    my $self = shift;

    $self->render('main');
};

websocket '/echo' => sub {
        my $self = shift;
        my $w;
        $w = AE::timer 3, 1, sub {
                    $self->send('Got it');
        };
        $self->on(finish => sub{ undef $w });
      #  $self->send(L<Mojolicious::Controller/strftime("Server $$: %a %b %e %H:%M:%S %Y", localtime));
};
app->start();

更新2:

为了澄清问题,SRI已经在Mojolicious中添加了以下文档:

当您使用“on”订阅事件或立即使用“send”发送消息时,WebSocket握手会自动响应101响应状态码以建立连接。

这解释了该情况。原始代码既未订阅事件,也没立即进行send操作。在几乎所有情况下,您至少要做其中的一种,在这种情况下就不是这样了 :-)


1
谢谢。但我想知道,在模板中使用url_for是一个很好的Mojo助手,但如果我想使用安全的websocket方案(wss,而不是我假设是Mojo的默认ws),该怎么办? - snoofkin
2
如果我没记错的话,你可以这样做:url_for('echo')->scheme('wss')->to_absurl_for 返回一个 Mojo::URL 对象,所以你可能需要阅读它的文档。 - Joel Berger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接