当数据长度未知时,从Perl中的套接字接收数据的最佳方法是什么?

时间:2010-07-18 09:08:00

标签: perl sockets recv

现在,我在一个循环中一次读取一个字符,直到我到达\0字符。有更好的方法吗?

5 个答案:

答案 0 :(得分:6)

将您的行结尾设置为\x{00}(\ 0),确保将其本地化,并在句柄上设置getline,如下所示:

{
    local $/ = "\x{00}";
    while (my $line = $sock->getline) {
       print "$line\n"; # do whatever with your data here
   }
}

答案 1 :(得分:2)

  

当数据长度未知时,从Perl中的套接字接收数据的最佳方法是什么?

用任何语言都无法解决这个问题。如果您不知道数据长度是多长,那么您无法知道何时从套接字接收完所有内容。

您唯一的希望是使用某种指标来确定数据是否已经“足够长”,因为数据开始进入,以确定数据流已经停止。但它并不完美。

答案 2 :(得分:2)

答案取决于协议。由于你的协议使用'\ 0'作为分隔符,你做的是正确的。我很确定Perl会为你处理缓冲,因此一次读取一个字符效率不高。

许多面向网络的协议位于具有长度的字符串之前。要读取这样的协议,您需要读取长度(通常是一个或两个字节,具体取决于协议规范),然后将多个字节读入字符串。

答案 3 :(得分:2)

您可以将FIONREADioctl一起使用。下面的程序连接到localhost上的SSH服务器并等待其问候:

#! /usr/bin/perl

use warnings;
use strict;

use subs 'FIONREAD';
require "sys/ioctl.ph";
use Socket;
socket my $s, PF_INET, SOCK_STREAM, getprotobyname "tcp"
  or die "$0: socket: $!";
connect $s, sockaddr_in 22, inet_aton "localhost"
  or die "$0: connect: $!";

my $rin = "";
vec($rin, fileno($s), 1) = 1;
my $nfound = select my$rout=$rin, "", "", undef;
die "$0: select: $!" if $nfound < 0;

if ($nfound) {
  my $size = pack "L", 0;
  ioctl $s, FIONREAD, $size
    or die "$0: ioctl: $!";

  print unpack("L", $size), "\n";
  sysread $s, my $buf, unpack "L", $size
    or die "$0: sysread: $!";

  my $length = length $buf;
  $buf =~ s/\r/\\r/g;
  $buf =~ s/\n/\\n/g;
  print "got: [$buf], length=$length\n";
}

示例运行:

$ ./howmuch
39
got: [SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu4\r\n], length=39

但您可能更喜欢使用IO::Socket::INETIO::Select模块,如下面与Google谈话的代码:

#! /usr/bin/perl

use warnings;
use strict;

use subs "FIONREAD";
require "sys/ioctl.ph";
use IO::Select;
use IO::Socket::INET;

my $s = IO::Socket::INET->new(PeerAddr => "google.com:80")
  or die "$0: can't connect: $@";

my $CRLF = "\015\012";
print $s "HEAD / HTTP/1.0$CRLF$CRLF" or warn "$0: print: $!";

my @ready = IO::Select->new($s)->can_read;
die "$0: umm..." unless $s == $ready[0];

my $size = pack "L", 0;
ioctl $s, FIONREAD, $size
  or die "$0: ioctl: $!";

print unpack("L", $size), "\n";
sysread $s, my $buf, unpack "L", $size
  or die "$0: sysread: $!";

my $length = length $buf;
$buf =~ s/\r/\\r/g;
$buf =~ s/\n/\\n/g;
print "got: [$buf], length=$length\n";

输出:

573
got: [HTTP/1.0 200 OK\r\nDate: Sun, 18 Jul 2010 12:03:48 GMT\r\nExpires: -1\r\nCache-Control: private, max-age=0\r\nContent-Type: text/html; charset=ISO-8859-1\r\nSet-Cookie: PREF=ID=6742ab80dd810a95:TM=1279454628:LM=1279454628:S=ewNg64020FbnGzHR; expires=Tue, 17-Jul-2012 12:03:48 GMT; path=/; domain=.google.com\r\nSet-Cookie: NID=36=kn2wtTD4UJ3MYYQ5uvA4iAsrS2wcrb_W781pZ1hrVUhUDHrIJTMg_kOgVKhjQnO5SM6MdC_jrRdxFRyXwyyv5N3Xja1ydhVLWWaYqpMHQOmGVi2K5qRWAKwDhCVRd8WS; expires=Mon, 17-Jan-2011 12:03:48 GMT; path=/; domain=.google.com; HttpOnly\r\nServer: gws\r\nX-XSS-Protection: 1; mode=block\r\n\r\n], length=573

答案 4 :(得分:0)

您可以使用sysread来读取可用的数据:

my $data;
my $max_length = 1000000;
sysread $sock, $data, $max_length;

Perl的read函数等待您请求的完整字节数或EOF。
这类似于libc stdio fread(3)

Perl的sysread函数在收到任何数据后立即返回 这与UNIX read(2)类似 请注意,sysread会绕过缓冲的IO,因此请勿将其与缓冲的read混合使用。

检查perldoc -f readperldoc -f sysread以获取更多信息。

对于此特定问题,最好按照最佳答案,并使用getline的行结尾\0,但如果没有,我们可以使用sysread终止性格。

这是一个小例子。它请求一个网页,并打印收到的第一个数据块。

#!/usr/bin/perl -w
use strict; use warnings;
use IO::Socket;

my $host = $ARGV[0] || 'google.com';
my $port = $ARGV[1] || 80;
my $sock = IO::Socket::INET->new(Proto => 'tcp', PeerAddr => $host, PeerPort => $port)
    or die "connect failed: $!";
$sock->autoflush(1);
# use HTTP/1.1, which keeps the socket open by default
$sock->print("GET / HTTP/1.1\r\nHost: $host\r\n\r\n");
my $reply;
my $max_length = 1000000;
# $sock->read($reply, $max_length);   # read would hang waiting for 1000000 bytes
my $count = $sock->sysread($reply, $max_length);
if (!defined $count) {
    die "read failed: $!";
}
print $reply;