代码之家  ›  专栏  ›  技术社区  ›  Rawley Fowler

如何解决IO::Socket::Async的UTF-8编码缺陷?

  •  0
  • Rawley Fowler  · 技术社区  · 3 年前

    我想知道是否有人知道如何绕过IO::Socket::Async的编码,特别是 this :

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

    这目前导致我的套接字忽略了消息上的最后一个字符,但我不知道如何解决这个问题。我试图转换 Connection 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);
    

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

    0 回复  |  直到 3 年前
        1
  •  8
  •   Jonathan Worthington    3 年前

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

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

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

    任何精心设计的网络协议都将提供一种方式来知道何时到达文本内容的末尾。一些协议,如HTTP,明确指定内容的字节长度,因此可以在字节级别工作( :bin )并且在看到那么多字节之后对结果进行解码。其他人可能会使用连接关闭或断线。

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