2010-07-18 5 views

답변

2

데이터 길이를 알 수없는 경우 Perl의 소켓에서 데이터를 수신하는 가장 좋은 방법은 무엇입니까?

어떤 언어에서도이 솔루션을 사용할 수 없습니다. 데이터 길이가 얼마나되는지 모르는 경우 소켓에서 소켓을 모두 수신했는지 여부를 알 수 없습니다.

데이터 흐름이 중단되었다는 결정을 내리기 위해 데이터가 들어 오기 시작한 후 "충분히 오래"되었는지를 확인하기 위해 일종의 메트릭을 사용해야합니다. 그러나 그것은 완벽하지 않습니다.

+0

각 메시지가 '\ o'로 끝나면 도움이 될까요? –

+2

* 문자가 데이터 스트림의 일부로 전송 될 수 없다는 것을 알고있는 한 * 데이터 끝 마커처럼 작동합니다.이 경우 데이터 길이를 알 필요가 없습니다. 어떤 경우에는 귀하의 솔루션이 유효합니다. –

+0

@Gal :'\ o'은 무엇입니까? '\ 0'을 의미합니까? – Svante

2

답변은 프로토콜에 따라 다릅니다. 프로토콜이 '\ 0'을 구분자로 사용하기 때문에 올바른 일을하고있는 것입니다. 펄이 당신을 위해 버퍼링을 처리한다는 것을 확신합니다. 그래서 한 번에 한 글자를 읽는 것이 비효율적이지 않습니다.

많은 네트워크 지향 프로토콜은 길이가있는 문자열 앞에옵니다. 이와 같은 프로토콜을 읽으려면 길이 (보통 프로토콜 사양에 따라 1 또는 2 바이트)를 읽은 다음 그 많은 바이트를 문자열로 읽습니다.

+1

PerlIO는 확실히 버퍼링을 처리하므로 1-char 읽기는 * syscall * 오버 헤드가 발생하지 않지만 여전히 Perl op 루프에서 시간을 낭비합니다 (문자열 연결 수는 말할 것도 없습니다 그 코드에 따라 일어날 수 있습니다). 마이크로 최적화는 아니지만'$ /'+'getline' 접근 방식이 훨씬 더 효율적이고 풍부하다. 그래서 성공한다. – hobbs

6

설정 \x{00} (\ 0)로 끝나는 라인을 현지화해야하고, 핸들 getline, 그래서 같은 :

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

당신은 ioctlFIONREAD을 사용할 수 있습니다. 아래 프로그램이 로컬 호스트에 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 모듈을 사용하여 선호하는 것 즉, 구글 회담 :

#! /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: [email protected]"; 

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"; 

출력 :

,536,913,632 10
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 
+0

사용할 수있는 데이터가 생기 자마자'sysread'가 반환되므로'FIONREAD '큰 사이즈의'sysread'를 호출하면됩니다. –

0

사용 가능한 무엇이든 데이터를 읽을 sysread를 사용할 수 있습니다

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

펄의 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; 
관련 문제