Perl6 IO :: Socket :: Async截断数据

Zar*_*uan 12 perl6 io-socket moarvm

我正在使用IO :: Socket :: Async在P6中重写我的P5套接字服务器,但收到的数据在结尾处被截断1个字符,并且在下一个连接上收到1个字符.来自Perl6 Facebook组的人(Jonathan Worthington)指出,这可能是由于字符串的性质和字节在P6中的处理方式截然不同.引:

在Perl 6中,字符串和字节的处理方式截然不同.值得注意的是,字符串在字形级别上起作用.接收Unicode数据时,不仅可能将多字节序列分组,还可以分割多码点序列.例如,一个数据包的末尾可能包含字母"a",而下一个数据包可能是组合的重音符号.因此,它不能安全地传递"a",直到看到下一个数据包如何开始.

我的P6正在MoarVM上运行

https://pastebin.com/Vr8wqyVu

use Data::Dump;
use experimental :pack;

my $socket = IO::Socket::Async.listen('0.0.0.0', 7000);

react {
    whenever $socket -> $conn {
        my $line = '';
        whenever $conn {

            say "Received --> "~$_;
            $conn.print: &translate($_) if $_.chars ge 100;  
            $conn.close;              

        }
    }
    CATCH {
        default {
            say .^name, ': ', .Str;
            say "handled in $?LINE";
        }
    }
}

sub translate($raw) {

    my $rawdata = $raw;
    $raw ~~ s/^\s+|\s+$//; # remove heading/trailing whitespace

    my $minus_checksum = substr($raw, 0, *-2);
    my $our_checksum = generateChecksum($minus_checksum);
    my $data_checksum = ($raw, *-2);

    # say $our_checksum;
    return $our_checksum;

}

sub generateChecksum($minus_checksum) {

    # turn string into Blob
    my Blob $blob = $minus_checksum.encode('utf-8');
    # unpack Blob into ascii list
    my @array = $blob.unpack("C*");
    # perform bitwise operation for each ascii in the list
    my $dec +^= $_ for $blob.unpack("C*");
    # only take 2 digits
    $dec = sprintf("%02d", $dec) if $dec ~~ /^\d$/;
    $dec = '0'.$dec if $dec ~~ /^[a..fA..F]$/;
    $dec = uc $dec;
    # convert it to hex
    my $hex = sprintf '%02x', $dec;
    return uc $hex; 

}
Run Code Online (Sandbox Code Playgroud)

结果

Received --> $$0116AA861013034151986|10001000181123062657411200000000000010235444112500000000.600000000345.4335N10058.8249E00015
Received --> 0
Received --> $$0116AA861013037849727|1080100018112114435541120000000000000FBA00D5122500000000.600000000623.9080N10007.8627E00075
Received --> D
Received --> $$0108AA863835028447675|18804000181121183810421100002A300000100900000000.700000000314.8717N10125.6499E00022
Received --> 7
Received --> $$0108AA863835028447675|18804000181121183810421100002A300000100900000000.700000000314.8717N10125.6499E00022
Received --> 7
Received --> $$0108AA863835028447675|18804000181121183810421100002A300000100900000000.700000000314.8717N10125.6499E00022
Received --> 7
Received --> $$0108AA863835028447675|18804000181121183810421100002A300000100900000000.700000000314.8717N10125.6499E00022
Received --> 7
Run Code Online (Sandbox Code Playgroud)

Jon*_*ton 12

首先,TCP连接是流,因此没有承诺发送的"消息"将作为接收端的等效"消息"接收.即使在考虑Perl 6行为之前,发送的内容也可以作为正常TCP行为的一部分进行拆分或合并.任何想要"消息"抽象的东西都需要在TCP流之上构建它(例如,通过以行的形式发送数据,或者以字节为单位发送大小,然后是数据).

在Perl 6中,通过套接字到达的数据公开为a Supply.A whenever $conn { }是简称whenever $conn.Supply { }(whenever将强制赋予a的任何东西Supply).默认值Supply是字符1,解码为UTF-8到Perl 6流中Str.正如您已经收到的答案所述,Perl 6中的字符串在字形级别工作,因此它将保留一个字符,以防下一个通过网络到达的东西是组合字符.这是您正在经历的"截断".(有些东西永远无法组合.例如,\n永远不能在其上放置组合字符.这意味着面向行的协议不会遇到这种行为,并且可以简单地实现whenever $conn.Supply.lines { }.)

有几种选择:

  • Do whenever $conn.Supply(:bin) { },它将提供二进制Blob对象,这将对应于OS传递给VM的内容.那就可以.decode随心所欲了.这可能是你最好的选择.
  • 例如,指定不支持组合字符的编码whenever $conn.Supply(:enc('latin-1')) { }.(但是,请注意,因为\r\n是1个字形,然后如果消息结束\r然后将被阻止以防下一个数据包出现a \n).

在这两种情况下,仍然可以在传输过程中拆分消息,但这些(完全和大部分分别)将避免字形规范化所需的保持一次性要求.

  • @ZarulZakuan该文档显示了例程[参数](https://docs.perl6.org/type/Parameter).在调用例程时,您传递[arguments](https://docs.perl6.org/language/functions#index-entry-Argument).作为调用过程的一部分,参数被"绑定"到参数但它们*之前*不是*相同*.`:$ bin`作为*参数*表示调用者必须将名为`bin`的对作为*参数*传递.您可以将`:$ bin`传递给`bin => $ bin`但是如果你想``$ bin`作为参数变成`bin => True`,你必须将`$ bin`初始化为`True`.或者你可以写`bin => True`或只是`:bin`. (2认同)