我正在使用IO :: Socket :: Async在P6中重写我的P5套接字服务器,但是接收到的数据最后被截断了1个字符,并且在下一个连接中收到了1个字符。来自Perl6 Facebook小组(乔纳森·沃辛顿)的人指出,这可能是由于P6中字符串和字节的处理方式非常不同。引用:
在Perl 6中,字符串和字节的处理方式非常不同。值得注意的是,字符串在字素级起作用。接收Unicode数据时,不仅可能会在数据包上拆分多字节序列,还会将多码点序列拆分。例如,一个小包的末尾可能会带有字母“ a”,而下一个小包可能是组合的重音符号。因此,在看到下一个数据包的开始方式之前,它不能安全地传递“ a”。
我的P6在MoarVM上运行
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;
}
结果
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
答案 0 :(得分:12)
首先,TCP连接是流,因此不能保证发送的“消息”将在接收端作为等效的“消息”接收。即使在考虑Perl 6行为之前,也可以将发送的内容拆分或合并为正常TCP行为的一部分。任何需要“消息”抽象的东西都需要将其构建在TCP流的顶部(例如,通过以行形式发送数据,或以字节为单位发送大小,然后是数据)。
在Perl 6中,通过套接字到达的数据显示为Supply
。 whenever $conn { }
是whenever $conn.Supply { }
的缩写(whenever
会强迫Supply
中给出的内容)。默认的Supply
是一个字符1,被解码为UTF-8到Perl 6 Str
的流中。正如您已经收到的答案中所指出的那样,Perl 6中的字符串在字素级别起作用,因此,如果通过网络到达的下一件事是组合字符,它将保留字符。这是您遇到的“截断”。 (有些东西永远不会被组合。例如,\n
上永远都不能放置组合字符。这意味着面向行的协议不会遇到这种情况。行为,可以简单地whenever $conn.Supply.lines { }
来实现。)
有两个可用选项:
whenever $conn.Supply(:bin) { }
,这将传递二进制Blob
对象,这将与操作系统传递给VM的对象相对应。然后可以根据需要.decode
。这可能是您最好的选择。whenever $conn.Supply(:enc('latin-1')) { }
。 (但是,请注意,由于\r\n
是1个字素,因此如果消息以\r
结尾,则下一个数据包与\n
一起出现时,该消息将被保留)。 在这两种情况下,仍然有可能在传输过程中拆分消息,但是这将(分别和大部分)避免字形规范化带来的“保持一站式”要求。