在Perl中通过网络发送二进制安全数据

时间:2011-10-14 18:54:25

标签: perl unicode utf-8 network-programming

我正在实现一个向服务器发送消息的网络客户端。消息是字节流,协议要求我事先发送每个流的长度。

如果我给出的消息(通过使用我的模块的代码)是一个字节字符串,那么length $string足够容易给出长度。但如果它是一串字符,我需要按摩它以获得原始字节。我现在正在做的基本上是这样的:

my $msg = shift;   # some message from calling code
my $bytes;
if ( utf8::is_utf8( $msg ) ) { 
    $bytes = Encode::encode( 'utf-8', $msg );
} else { 
    $bytes = $msg;
}

my $length = length $bytes;

这是处理此问题的正确方法吗?它似乎工作到目前为止,但我还没有做过任何严肃的测试。这种方法有哪些潜在的缺陷?

由于

3 个答案:

答案 0 :(得分:4)

你不应该猜测你的输入是什么。 定义您的代码以接受字节字符串或Unicode字符串,并将其留给调用者将输入转换为正确的格式(或者为调用者提供某种方式来指定他们的字符串类型'重新提供)。

如果您将代码定义为接受字节字符串,那么\xFF以上的任何字符都是错误的。

如果您将代码定义为接受Unicode字符串,那么您可以将它们转换为Encode::encode_utf8()的字节(并且无论Perl如何在内部表示它们都应该这样做。)

在任何情况下,调用utf8::is_utf8()通常都是错误的 - 您的程序不应该关心字符串的内部表示,而只关心它们包含的实际数据(字符序列)。其中一些字符(特别是\x80\xFF范围内的那些字符)是否由一个或两个字节在内部表示无关紧要。

聚苯乙烯。阅读perldoc Encode可能有助于澄清Perl中字节和字符的问题。

答案 1 :(得分:1)

发件人:

use Encode qw( encode_utf8 );

sub pack_text {
   my ($text) = @_;
   my $bytes = encode_utf8($text);
   die "Text too long" if length($bytes) > 4294967295;
   return pack('N/a*', $bytes);
}

接收者:

use Encode qw( decode_utf8 );

sub read_bytes {
   my ($fh, $to_read) = @_;
   my $buf = '';
   while ($to_read > 0) {
      my $bytes_read = read($fh, $buf, $to_read, length($buf));
      die $! if !defined($bytes_read);
      die "Premature EOF" if !$bytes_read;
      $to_read -= $bytes_read;
   }
   return $buf;
}

sub read_uint32 {
   my ($fh) = @_;
   return unpack('N', read_bytes($fh, 4));
}

sub read_text {
   my ($fh) = @_;
   return decode_utf8(read_bytes($fh, read_uint32($fh)));
}

答案 2 :(得分:0)

perldoc -f length曾经说过,回到v5.8,

  

...您将获得字符数,而不是字节数。   要获得以字节为单位的长度,请使用"do { use bytes; length(EXPR) }",   见bytes

length的现代文档未提及bytes

  

length()通常会处理   逻辑字符,而不是物理字节。对于多少字节a   编码为UTF-8的字符串将占用,使用   "length(Encode::encode_utf8(EXPR))"(你必须“使用   编码“首先”。请参阅Encodeperlunicode

但我不认为弃用do { use bytes; ... }解决方案。