如何解决IO::Socket::Async UTF-8编码下末尾字符不输出问题?
解决IO::Socket::Async的UTF-8编码缓存问题
IO::Socket::Async的Supply在处理UTF-8编码时,会缓存可能与后续字节组合成完整Unicode字素的单字符(比如普通字母"a"),只有遇到明确的字素边界(如\n、空字节)时才会输出缓存内容,这就导致无边界的消息丢失最后一个字符。以下是几种针对性的解决方案:
方案1:使用内置的lines方法(推荐,适用于带换行符的文本协议)
如果你的协议以换行符作为消息终止符,直接用$connection.lines替代Supply即可,它会自动处理字素边界,确保完整输出每一行内容:
sub listen(Int $port) { react { whenever IO::Socket::Async.listen('0.0.0.0', $port) -> $connection { whenever $connection.lines -> $line { say $line; $connection.print: "$line\n"; } } } } listen(9999);
方案2:手动维护缓冲区(适用于无固定终止符的场景)
自己维护一个字符串缓冲区,每次收到数据后提取所有完整的Unicode字素输出,剩余不完整的内容留在缓冲区;当连接关闭时,强制输出剩余缓存:
sub listen(Int $port) { react { whenever IO::Socket::Async.listen('0.0.0.0', $port) -> $connection { my $buffer = ''; whenever $connection.Supply -> $chunk { $buffer ~= $chunk; # 提取所有完整的Unicode字素,留下不完整的内容在缓冲区 while $buffer ~~ /^ (.*? \N) / { say $0; $connection.print: $0; $buffer = $buffer.substr($0.chars); } } # 连接关闭时输出剩余缓存 whenever $connection.close-status { if $buffer { say $buffer; $connection.print: $buffer; } } } } } listen(9999);
其中\N用于匹配一个完整的Unicode字素,确保不会拆分需要组合的字符序列。
方案3:直接处理字节流(仅适用于确定无组合字符的场景)
如果你的场景中绝对不会出现Unicode组合字符,可以跳过IO::Socket::Async的字素处理逻辑,直接获取字节流并手动解码:
sub listen(Int $port) { react { whenever IO::Socket::Async.listen('0.0.0.0', $port) -> $connection { # 以字节模式获取数据流,手动解码为UTF-8 whenever $connection.Supply(:bin) -> $bin_data { my $data = $bin_data.decode('utf-8'); say $data; $connection.print: $data; } } } } listen(9999);
注意:这种方式如果遇到未完成的UTF-8字节序列,会抛出解码错误,仅适用于能保证所有数据都是完整UTF-8字节的场景。
关于你尝试用Channel加假\n无效的原因:IO::Socket::Async的字素缓存是在Supply底层处理的,后续追加\n无法触底底层缓存的输出,必须从Supply的处理逻辑本身入手解决。
内容的提问来源于stack exchange,提问作者Rawley Fowler
相关产品推荐
相关产品推荐

