下面的客户端程序从WebSocket服务器接收消息。
它不发送任何消息。
use v6;
use Cro::WebSocket::Client;
constant WS-URL = 'ws://localhost:20000/status';
constant TIMEOUT-TO-CONNECT = 5; # seconds
my $timeout;
my $connection-attempt;
await Promise.anyof(
$connection-attempt = Cro::WebSocket::Client.connect(WS-URL),
$timeout = Promise.in(TIMEOUT-TO-CONNECT));
if $timeout.status == Kept
{
say "* could not connect to server in ', TIMEOUT-TO-CONNECT, ' seconds";
exit 1;
}
if $connection-attempt.status != Kept
{
my $cause = $connection-attempt.cause;
say '"* error when trying to connect to server';
say '"* --------------------------------------';
# $cause is a long string, how do we get a simple numeric code ?
say $cause;
say '"* ======================================';
exit 1;
}
my $connection = $connection-attempt.result;
my $peer = 'localhost:20000';
say '* connected with ', $peer;
react
{
whenever $connection.messages -> $message
{
my $body = await $message.body;
say '* received message=[' ~ $body ~ '] from server';
LAST { say '* LAST'; done; }
QUIT { default { say '* QUIT'; done; }}
}
CLOSE { say '* CLOSE: leaving react block';}
} # react
use Cro::HTTP::Router;
use Cro::HTTP::Server;
use Cro::HTTP::Router::WebSocket;
my $application =
route
{
get -> 'status'
{
web-socket -> $incoming
{
my $counter = 0;
my $timer = Supply.interval(1);
supply
{
whenever $incoming -> $thing
{
LAST { note '* LAST: client connection was closed'; done; }
QUIT { default { note '* QUIT: error in client connection'; done; } }
}
whenever $timer
{
$counter++;
say '* sending message ', $counter;
emit $counter.Str;
}
CLOSE { say '* CLOSE: leaving supply block'; }
} # supply
} #incoming
} # get -> status
}
my $server = Cro::HTTP::Server.new: :port(20000), :$application;
$server.start;
say '* serving on port 20000';
react whenever signal(SIGINT)
{
$server.stop;
exit;
}
现在,当服务器关闭时(例如,通过Ctrl + C),客户端什么也看不到。
在客户端中将CRO_TRACE = 1设置为:
TRACE(anon 2)] Cro::WebSocket::MessageParser EMIT WebSocket Message - Text
* received message=[4] from server
[TRACE(anon 1)] Cro::TCP::Connector DONE
[TRACE(anon 2)] Cro::WebSocket::FrameParser DONE
[TRACE(anon 2)] Cro::WebSocket::MessageParser DONE
[TRACE(anon 1)] Cro::HTTP::ResponseParser DONE
^C
客户什么都没显示(然后我取消了)。
因此,问题是:客户应如何应对这种情况?
编辑了问题,现在显示了服务器代码
另外,我在Fedora 28中。
首次取消服务器时,netstat显示
$ netstat -ant | grep 20000
tcp6 0 0 ::1:20000 ::1:56652 TIME_WAIT
$
Tcpdump显示
IP6 ::1.20000 > ::1.56652: Flags [F.], seq 145, ack 194, win 350, options [nop,nop,TS val 1476681452 ecr 1476680552], length 0
IP6 ::1.56652 > ::1.20000: Flags [F.], seq 194, ack 146, win 350, options [nop,nop,TS val 1476681453 ecr 1476681452], length 0
IP6 ::1.20000 > ::1.56652: Flags [.], ack 195, win 350, options [nop,nop,TS val 1476681453 ecr 1476681453], length 0
似乎缺少从客户端到服务器的最后一个ACK,我想客户端没有关闭连接。
此外,我很好奇为何Cro会默认使用IPv6。
答案 0 :(得分:4)
这是自发布此问题以来已修复的错误,但是由于问题的这一部分可能会使人们在Raku中处理网络时感到困惑,所以我留下了答案。
如果此外,我很好奇为什么Cro选择默认使用IPv6。
localhost
是localhost
文件中hosts
的第一个地址,则它将首先解析为IPv6地址。撰写本文时,IO::Socket::Async
(Cro在内部使用)仅允许将PF_UNSPEC
指定为一个族,并且主机名解析结果中将使用的唯一地址是列表中的第一个地址收到的地址。作为我的IP6NS grant和problem solving issue to improve how DNS is handled工作的一部分,将来会对此进行更改,但是就目前而言,如果只想使用IPv4 / IPv6,则应指定{{1 }} / 127.0.0.1
而不是使用::1
(或者如果机器不同,则将地址解析为机器的地址)。