Perl6 IO :: Socket :: Async усекает данные - PullRequest
0 голосов
/ 24 ноября 2018

Я переписываю свой сервер сокетов P5 в P6, используя IO :: Socket :: Async, но полученные данные получили усеченный 1 символ в конце, и этот символ был получен при следующем соединении.Кто-то из группы Perl6 в Facebook (Джонатан Уортингтон) отметил, что это может быть связано с природой строк и байтов, которые обрабатываются в P6 очень по-разному.Цитируется:

В Perl 6 строки и байты обрабатываются совершенно по-разному.Примечательно, что струны работают на уровне графемы.При получении данных Unicode не только возможно, что многобайтовая последовательность будет разделена на пакеты, но также и последовательность с несколькими кодами.Например, один пакет может иметь букву «а» в конце, а следующий будет сочетать острый акцент.Следовательно, он не может безопасно передать «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; 

}

Результат

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

1 Ответ

0 голосов
/ 24 ноября 2018

Прежде всего, TCP-соединения являются потоками, поэтому нет никаких обещаний, что отправленные «сообщения» будут получены как эквивалентные «сообщения» на принимающей стороне.Вещи, которые отправляются, могут быть разделены или объединены как часть нормального поведения TCP, даже до рассмотрения поведения Perl 6.Все, что требует абстракции «сообщений», должно создавать его поверх потока TCP (например, отправляя данные в виде строк или отправляя размер в байтах с последующими данными).

В Perl6, данные, поступающие через сокет, отображаются как Supply.A whenever $conn { } - это сокращение от whenever $conn.Supply { } (whenever приведет к тому, что ему дано в Supply).По умолчанию Supply является символьным, декодированным как UTF-8 в поток Perl 6 Str.Как уже отмечалось в ответе, который вы уже получили, строки в Perl 6 работают на уровне графемы, поэтому он сохранит символ в случае, если следующая вещь, которая появляется по сети, является символом объединения.Это «усечение», которое вы испытываете.(Есть некоторые вещи, которые никогда не могут быть объединены . Например, \n никогда не может иметь объединенный символ, помещенный на него. Это означает, что линейно-ориентированные протоколы не встретят такого рода поведение, иможет быть реализовано как просто whenever $conn.Supply.lines { }.)

Существует несколько доступных опций:

  • Do whenever $conn.Supply(:bin) { }, которые будут доставлять двоичные Blob объекты, которые будут соответствоватьна что ОС перешла на ВМ.Это может быть тогда .decode, как хотелось бы.Это, вероятно, ваш лучший выбор.
  • Укажите кодировку, которая не поддерживает комбинирование символов, например whenever $conn.Supply(:enc('latin-1')) { }.(Тем не менее, обратите внимание, что, поскольку \r\n - это 1 графема, то, если сообщение заканчивается на \r, то оно будет задержано, если следующий пакет будет сопровождаться \n).

В обоих случаях все еще возможно разделение сообщений во время передачи, но это (полностью и в основном, соответственно) позволит избежать требования "оставайся одним", которое влечет за собой нормализация графемы.

...