如何冲洗打开的管柄?

时间:2016-08-14 13:23:27

标签: perl

我正在尝试使用open模块中的autoflush()flush()方法来清除从IO::Handle获取的管道句柄,但我认为它不起作用。这是一个例子:

host.pl

use feature qw(say);
use strict;
use warnings;

my $client_pid = open ( my $fh, '|-', 'client.pl' ) 
    or die "Could not open client: $!";
#$fh->autoflush(1);  # adding this line does not help
sleep 2;
say "Host: sent message";
print $fh "Hello";
#print $fh "Hello\n";  # adding a newline works fine
$fh->flush() or warn "$!";  # this does not work
sleep 2;
say "Host exits.";
close $fh;

client.pl

use feature qw(say);
use strict;
use warnings;

say "Client running..";
chomp (my $line = <STDIN>);
say "Client got line: '$line'";
sleep 1;
say "Client exits..";

运行host.pl的输出是:

Client running..
Host: sent message
Host exits.
Client got line: 'Hello'
Client exits..

预期输出为:

Client running..
Host: sent message
Client got line: 'Hello'
Client exits..
Host exits.

我知道我可以通过在要打印的字符串末尾添加换行来解决此问题:

print $fh "Hello\n";

但我很好奇为什么$fh->flush()不能在这里工作?

1 个答案:

答案 0 :(得分:3)

数据立即发送到客户端,但客户端等待换行符到达。

readline(其中<>是程序中的快捷方式)读取,直到遇到换行符才会返回(虽然更改$/可以更改该行为。如果您想要调用数据可用后立即返回,请使用sysread

use BLOCK_SIZE => 64*1024;

say "Client running..";
while (1) {
   my $rv = sysread(\*STDIN, my $buf, BLOCK_SIZE);
   die($!) if !defined($rv);
   last if !$rv;
   say "Got: $buf";
}

请注意,单次打印可能会导致以多个块的形式接收数据。在实践中,尤其是使用套接字而不是管道时,您需要某种方式来构建消息以便可靠地识别它们。例如,以下客户端期望以哨兵终止的消息(哨兵是换行符):

use BLOCK_SIZE => 64*1024;

say "Client running..";
my $buf = '';
while (1) {
   my $rv = sysread(\*STDIN, $buf, BLOCK_SIZE, length($buf));
   die($!) if !defined($rv);
   last if !$rv;
   while ($buf =~ s/^([^\n]*)\n//) {
      my $msg = $1;
      say "Got: $msg";
   }

   say "Got a partial message" if length($buf);
}

die("Premature EOF\n") if length($buf);

尝试发送:

$fh->autoflush();
print($fh "abc");
sleep(1);
print($fh "def\n");
sleep(1);
print($fh "ghi\njkl\nmno");
sleep(1);
print($fh "pqr\n");

这可以适用于处理长度加前缀的消息或任何其他消息格式。