我想我在Mojolicious WebSocket中做了一些愚蠢的事情并且有一个简单的解决方案。
我有一个表单,当POSTed启动WebSocket接收进度文本时。问题是在我发出finish()命令之前连接没有完成。此时,我的浏览器实际上打开了套接字并接收了所有消息。
我认为我在服务器端做错了什么,但我不能排除javascript作为问题。
的javascript
$(document).ready(function() {
$("form.report_form").submit(function(event) {
event.preventDefault();
/* do the POSTy things */
var ws = new WebSocket($('#progress_url').val());
ws.onopen = function(){ alert('websocket is ready for business')};
ws.onmessage = function (event) {
console.log(event.data);
}
});
/* stuff to configure the form */
});
Mojolicious Controller
package WebLOC::Controller::Progress;
use Mojo::Base 'Mojolicious::Controller';
sub tail {
my $self = shift;
my ($guid)
= $self->url_for
=~ /([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})/;
my $filename = "/tmp/$guid.out";
my $fh;
my $is_open = open( $fh, '<', $filename );
unless ($is_open) { # try one more time
sleep 1;
$is_open = open( $fh, '<', $filename );
}
if ($is_open) {
$self->inactivity_timeout( 5 * 60 );
# shouldn't need a subscription, but...
$self->on( finish => sub { } );
while ( my $msg = <$fh> ) {
chomp $msg;
$self->send(
$msg => sub { $self->app->log->debug('I am drained') } );
$self->app->log->debug("read: $msg");
}
}
else {
$self->app->log->error("Can't open file $filename: $!");
return;
}
$self->finish;
return 0;
}
1;
示例应用
select STDOUT;
$|=1;
say "1 of 3";
sleep 3;
say "2 of 3";
sleep 3;
say "3 of 3";
sleep 3;
exit;
Mojolicious Log
[Wed Nov 22 14:35:31 2017] [debug] GET "/progress/53898C2E-CFC8-11E7-B15D-BCDD6A63D34C"
[Wed Nov 22 14:35:31 2017] [debug] Routing to controller "WebLOC::Controller::Auth" and action "check"
[Wed Nov 22 14:35:31 2017] [debug] Routing to controller "WebLOC::Controller::Progress" and action "tail"
[Wed Nov 22 14:35:31 2017] [debug] 101 Switching Protocols (0.008343s, 119.861/s)
[Wed Nov 22 14:35:33 2017] [debug] read: 1 of 3
[Wed Nov 22 14:35:36 2017] [debug] read: 2 of 3
[Wed Nov 22 14:35:39 2017] [debug] read: 3 of 3
[Wed Nov 22 14:35:42 2017] [debug] I am drained
[Wed Nov 22 14:35:42 2017] [debug] I am drained
[Wed Nov 22 14:35:42 2017] [debug] I am drained
日志显示文件读取(和WS发送)是实时发生的,但是发送不会被耗尽直到结束。我还可以看到WebSocket没有显示established()。在客户端,我不会获得onopen alert(),也不会获取onmessage控制台日志,直到服务器上的连接关闭为止。
解决 在控制器退出之前,我可以告诉WebSocket的最佳效果尚未准备就绪。我认为有一种方法可以在此之前强制它工作,但我肯定无法弄明白。
我改变了我的设计。我将tx()对象存储在一个存储哈希中,而不是试图立即将数据发送到套接字。然后,我将脚本的输出发送到该事务。
Mojolicious Application
$self->defaults( layout => 'default', websockets => {}, );
...
$auth->websocket('/progress/:guid')->to('progress#websocket');
WebSocket控制器
sub websocket {
my $self = shift;
my $guid = $self->stash('guid');
$self->on( finish => sub { delete $self->stash->{websockets}{$guid} } );
$self->stash->{websockets}{$guid} = $self->tx;
}
报告控制器
my $fork = Mojo::IOLoop::ReadWriteFork->new;
$fork->on(
read => sub {
my ( undef, $msg ) = @_;
chomp $msg;
my $websocket = $self->stash->{websockets}{$key};
$websocket->send($msg) if $websocket;
}
);
$fork->on(
close => sub {
my ( undef, $err ) = @_;
my $websocket = $self->stash->{websockets}{$key};
$websocket->finish if $websocket;
}
);