现在,我在一个循环中一次读取一个字符,直到我到达\0
字符。有更好的方法吗?
答案 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)
您可以将FIONREAD
与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";
}
示例运行:
$ ./howmuch 39 got: [SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu4\r\n], length=39
但您可能更喜欢使用IO::Socket::INET
和IO::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 read
和perldoc -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;