4

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

4

5 回答 5

6

Set your line ending to \x{00} (\0), be sure to localise it, and getline on the handle, like so:

{
    local $/ = "\x{00}";
    while (my $line = $sock->getline) {
       print "$line\n"; # do whatever with your data here
   }
}
于 2010-07-18T10:22:17.303 回答
2

你可以使用FIONREADwith ioctl。下面的程序连接到 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";
}

样品运行:

$ ./多少
39
得到:[SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu4\r\n],长度=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
得到:[HTTP/1.0 200 OK\r\n日期:2010 年 7 月 18 日星期日 12:03:48 GMT\r\n过期:-1\r\n缓存控制:私有,max-age=0\r\n内容-类型:文本/html;charset=ISO-8859-1\r\nSet-Cookie: PREF=ID=6742ab80dd810a95:TM=1279454628:LM=1279454628:S=ewNg64020FbnGzHR; 到期=格林威治标准时间 2012 年 7 月 17 日星期二 12:03:48;路径=/; domain=.google.com\r\nSet-Cookie: NID=36=kn2wtTD4UJ3MYYQ5uvA4iAsrS2wcrb_W781pZ1hrVUhUDHrIJTMg_kOgVKhjQnO5SM6MdC_jrRdxFRyXwyyv5N3Xja1ydhVLWWaYqpMHQOmGVi2K5qRWAKwDhCVRd8WS; expires=星期一,2011 年 1 月 17 日 12:03:48 GMT;路径=/; 域=.google.com;HttpOnly\r\n服务器:gws\r\nX-XSS-保护:1;模式=块\r\n\r\n],长度=573
于 2010-07-18T12:04:33.887 回答
2

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

用任何语言都不可能有一个健全的解决方案。如果你不知道数据长度有多长,那么你不可能知道你什么时候从套接字接收完所有数据。

您唯一的希望是使用某种度量来确定自数据开始进入以来它是否已经“足够长”,从而做出数据流已停止的决定。但它不会是完美的。

于 2010-07-18T09:16:54.917 回答
2

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

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

于 2010-07-18T09:30:58.573 回答
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;
于 2016-09-22T07:25:17.093 回答