Cro WebSocket客户端看不到服务器何时出局

zen*_*nix 8 perl6 cro

下面的客户端程序从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
Run Code Online (Sandbox Code Playgroud)

服务器

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;
}
Run Code Online (Sandbox Code Playgroud)

现在,当服务器熄灭时(例如,通过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  
Run Code Online (Sandbox Code Playgroud)

客户端没有显示任何内容(然后我取消了它).

所以,问题是:客户应该如何处理这种情况?

UPDATE

编辑了这个问题,现在显示服务器代码
另外,我在Fedora 28.当我第一次取消服务器时,netstat显示

$ netstat -ant | grep 20000
tcp6       0      0 ::1:20000               ::1:56652               TIME_WAIT  
$
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

似乎从客户端到服务器的最后一个ACK丢失,我猜客户端没有关闭连接.

此外,我很好奇为什么Cro默认选择使用IPv6.

Kai*_*epi 4

这是一个自发布此问题以来已修复的错误,但我留下了一个答案,因为问题的这一部分可能会在处理 Raku 中的网络时使人们感到困惑:

另外,我很好奇为什么 Cro 默认选择使用 IPv6。

localhost如果文件localhost中的第一个地址就是 IPv6 地址,则将首先解析为 IPv6 地址hosts。截至撰写本文时,IO::Socket::Async(Cro 内部使用的)仅允许PF_UNSPEC指定为一个系列,并且从主机名解析结果中使用的唯一地址是收到的地址列表中的第一个地址。这将在未来的某个时候进行更改,作为我的IP6NS 资助工作的一部分,并解决问题以改进 DNS 的处理方式,但目前,如果您只想使用 IPv4/IPv6,则应该指定127.0.0.1/::1代替使用localhost(或者您的机器将其解析为不同的地址)。