Мне было интересно, знает ли кто-нибудь, как обойти кодировку IO::Socket::Async, особенно недостатки, описанные это:
Например, если используется кодировка 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, вы увидите, что последний байт игнорируется.
@zentrunix Я добавил простой код, который воспроизводит эту проблему.
@zentrunix Похоже, :bin сработало, тогда я просто беру Buf[uint8] и делаю .decode.Str, и это работает как шарм. Спасибо!
«Похоже, :bin сработало, тогда я просто беру Buf[uint8] и делаю .decode.Str, и это работает как шарм». Если данные не являются ASCII или подобными, это означает, что вы нарушаете кодировку Unicode, поэтому вы получите смесь правильных строк, (молча) неправильных строк и исключений декодирования.





Это не «недостаток»; это просто Раку, отражающий, как работает Unicode. Если вы знаете, что вам нужно обрабатывать только ASCII или Latin-1, укажите это:
whenever $connection.Supply(:enc<ascii>) -> $data { # or :enc<latin-1>
...
}
Если вы хотите обрабатывать текст Unicode, то необходимо иметь дело с тем фактом, что получение, например, кодовой точки для буквы «а» не дает достаточно информации для передачи полного символа, поскольку следующая кодовая точка, полученная в следующем package может быть объединяющим символом, например, акут ставится на «a». Обратите внимание, что Raku Str — это структура данных на уровне символов (в других языках строки часто представляют собой байты или кодовые точки, что создает различные проблемы, которые в значительной степени невидимы для тех, кто заботится только об английском тексте!)
Любой хорошо спроектированный сетевой протокол предоставит способ узнать, когда достигнут конец текстового содержимого. Некоторые протоколы, такие как HTTP, явно указывают длину содержимого в байтах, поэтому можно работать с уровнем байтов (:bin) и декодировать результат, увидев такое количество байтов. Другие могут использовать закрытие соединения или разрывы строк.
В заключение, семантика строк или IO::Socket::Async (и в других местах в Raku) сами по себе не являются проблемой, но они могут выявить проблемы дизайна в протоколах.
Да, я, вероятно, мог бы сформулировать это лучше. Извини за это. Ваше объяснение было превосходным, и оно помогло мне полностью понять, что происходит с UTF-8 и почему так должно быть. Спасибо!
Следует отметить, что использование «UTF-8» в этих описаниях немного вводит в заблуждение: эта проблема может возникнуть во всем, что поддерживает все многие/большинство/все кодовые точки Unicode (например, UTF-8, UTF-16, даже UCS-2) и заботится о границах графемы (об этом заботится больше вещей, чем вы думаете). Это совершенно не связано с UTF-8 как кодировкой.
@JoachimSauer Согласен. Это не связано с UTF-8, это связано практически с любой кодировкой Unicode. Я был удивлен цитатой документа в вопросе, но затем увидел, что фактическое полное предложение документа начинается «Например», которое было исключено из предоставленной цитаты. С документом все в порядке - хороший пример - но проблема, как вы подчеркиваете, является общей для Unicode. Я только что отредактировал вопрос (вставив «Например»), чтобы попытаться отвлечь читателей от мысли, что речь идет о UTF-8.
покажите код, который вы используете ... вы пытались использовать «наречие»
:bin, чтобы буфер не рассматривался как UTF-8?