为什么 IO::Socket::Async 不发出尾随“a”?

Raw*_*ler 4 unicode utf-8 raku

我想知道是否有人知道如何绕过 IO::Socket::Async 的编码,特别是以下描述的缺点:

例如,如果使用 UTF-8 编码并且数据包中的最后一个字节解码为“a”,则不会发出此消息,因为下一个数据包可能包含应一起形成单个字素的组合字符。控制字符(例如 \n)始终充当字素边界,因此任何使用换行符或空字节作为终止符的基于文本的协议都不需要特殊考虑。

目前,这导致我的套接字省略消息中的最后一个字符,但我不确定如何解决此问题。我尝试将 转换Connection为 a Channel,然后将一个哑巴输入\n其中,模拟消息输入的结束,但这不起作用。如何解决 UTF-8 编码中的这个问题?

这是重现这一点的 MVP:

sub listen(Int $port) {
  react {
    whenever IO::Socket::Async.listen('0.0.0.0', $port) -> $connection {
      whenever $connection.Supply -> $data {
        say $data;
        $connection.print: $data;
      }
    }
  }
}

listen(9999);
Run Code Online (Sandbox Code Playgroud)

现在,如果您使用任何不以 结尾的数据访问本地计算机上的端口 9999,\n您将看到最后一个字节被忽略。

Jon*_*ton 8

这不是“缺点”,而是“缺点”。Raku 反映了 Unicode 的工作原理。如果您知道只需要处理 ASCII 或 Latin-1,请指定:

whenever $connection.Supply(:enc<ascii>) -> $data { # or :enc<latin-1>
    ...
}
Run Code Online (Sandbox Code Playgroud)

如果想要处理 Unicode 文本,那么有必要处理这样一个事实:接收(例如,字母“a”的代码点)没有提供足够的信息来传递完整的字符,因为下一个代码点在下一个中接收packet 可能是一个组合字符,例如要放在“a”上的重音符号。请注意,RakuStr是一种字符级数据结构(在其他语言中,字符串通常是字节或代码点,这会产生不同的问题,而这些问题对于那些只关心英文文本的人来说基本上是看不见的!)

任何设计良好的网络协议都会提供一种方法来了解何时到达文本内容的末尾。某些协议(例如 HTTP)显式指定内容的字节长度,因此可以在字节级别 ( :bin) 工作并在看到这么多字节后解码结果。其他人可能会使用连接关闭或换行符。

总之,字符串语义或IO::Socket::Async(以及 Raku 中的其他地方)本身并不是问题,但它们可能会显示协议中的设计问题。

  • 是的,我可能可以用更好的措辞。对于那个很抱歉。您的解释非常好,它帮助我完全理解 UTF-8 的情况以及为什么需要这样。谢谢你! (2认同)
  • 应该指出的是,在这些描述中使用“UTF-8”有点误导:在支持所有许多/大多数/所有 Unicode 代码点(即 UTF-8、UTF-16 甚至 UCS-2)的任何内容中都会出现此问题,并且关心字素边界(关心这一点的事情比你想象的要多)。它与 UTF-8 作为编码完全无关。 (2认同)