如果我从套接字中读取内容,我该怎么办?

时间:2015-10-18 15:24:16

标签: windows perl sockets

以下代码应该从套接字中读取一些未确定的行数。

use warnings;
use strict;

use IO::Socket::INET;

my $server = shift;
my $port   = shift;

my $sock  = new IO::Socket::INET (
               PeerAddr    => $server,
               PeerPort    => $port,
               Proto       => 'tcp',
               Timeout     =>  1,
               Blocking    =>  0
           ) 
           or die "Could not connect";

while (my $in = <$sock>) {
  print "$in";
}

print "Received last line\n";

不幸的是,尽管我设置了$in = <$sock>并且服务器不再发送任何文本,但Blocking => 0部分仍在阻止。因此,Received last line不会被打印出来。

所以,我尝试用use IO::Select来改善行为:

use warnings;
use strict;

use IO::Socket::INET;
use IO::Select;

my $server = shift;
my $port   = shift;

my $sock  = new IO::Socket::INET (
               PeerAddr    => $server,
               PeerPort    => $port,
               Proto       => 'tcp',
               Timeout     =>  1,
               Blocking    =>  1
           ) 
           or die "Could not connect";

my $select = new IO::Select;
$select -> add($sock);

sleep 1;
while ($select -> can_read) {
  my $in = <$sock>;
  print $in;
}

第二种方法只打印第一个发送的行,然后似乎永远阻止。

由于我看到这样的例子有效,我认为问题是Windows,我试图运行这些脚本。

有没有办法实现非阻塞读取?

1 个答案:

答案 0 :(得分:1)

使用select(由can_read使用)时,它无法跟进阻止IO的目的。您还必须避免缓冲IO,因为系统(即select)不知道库缓冲区中的任何数据。这意味着您无法将selectreadreadline(又名<><$fh>)和eof混合在一起。您必须使用sysread

my %clients;
for my $fh ($select->can_read()) {
   my $client = $clients{$fh} //= {};
   our $buf; local *buf = $client->{buf} //= '';   # alias my $buf = $client->{buf};
   my $rv = sysread($sock, $buf, 64*1024, length($buf));
   if (!defined($rv)) {
      my $error = $!;
      $select->remove($fh);
      delete($clients{$fh});
      # ... Handle error ...
      next;
   }

   if (!$rv) {
      $select->remove($fh);
      delete($clients{$fh});
      # ... Handle EOF ...         # Don't forget to check if there's anything in $buf.
      next;
   }

   ... remove any complete messages from $buf and handle them ...
}

如果您想一次阅读一行,请使用

while ($buf =~ s/^([^\n]*)\n//) {
   process_msg($client, $1);
}