【问题标题】:Perl: How to get IO::Socket::INET timeout after X seconds?Perl:如何在 X 秒后获取 IO::Socket::INET 超时?
【发布时间】:2010-08-25 21:38:45
【问题描述】:

我正在尝试使用无效端口连接到某个主机,并且我想在 X 秒后超时。该怎么做?

我的代码:

 $sock = new IO::Socket::INET(
                  PeerAddr => $_[0],
    PeerPort => $_[1],
    Proto => 'tcp',
    Timeout => 2
    );

【问题讨论】:

    标签: perl sockets networking inet


    【解决方案1】:

    如果你检查你会看到的代码(我从我的 Ubuntu 10.04 复制它):

            my $timeout = ${*$sock}{'io_socket_timeout'};
    #       my $before = time() if $timeout;
    
            undef $@;
            if ($sock->connect(pack_sockaddr_in($rport, $raddr))) {
    #            ${*$sock}{'io_socket_timeout'} = $timeout;
                return $sock;
            }
    
            return _error($sock, $!, $@ || "Timeout")
                unless @raddr;
    
    #       if ($timeout) {
    #           my $new_timeout = $timeout - (time() - $before);
    #           return _error($sock,
    #                         (exists(&Errno::ETIMEDOUT) ? Errno::ETIMEDOUT() : $EINVAL),
    #                         "Timeout") if $new_timeout <= 0;
    #           ${*$sock}{'io_socket_timeout'} = $new_timeout;
    #        }
    

    显然,超时内容已被注释掉,以便解释它被忽略的原因。

    我发现了一个可追溯到 2003 年的 post,并在其中进行了讨论。一个建议(在底部)是在一个被警报信号终止的 eval 块中打开套接字:

    eval { 
      local $SIG{ALRM} = sub { die 'Timed Out'; }; 
      alarm 3; 
      my $sock = IO::Socket::INET->new( 
        PeerAddr => inet_ntoa( gethostbyname($host) ), 
        PeerPort => 'whois', 
        Proto => 'tcp', 
        ## timeout => , 
      );
      $sock->autoflush;   
      print $sock "$qry\015\012"; 
      undef $/; $data = <$sock>; $/ = "\n"; 
      alarm 0; 
    }; 
    alarm 0; # race condition protection 
    return "Error: timeout." if ( $@ && $@ =~ /Timed Out/ ); 
    return "Error: Eval corrupted: $@" if $@; 
    

    不是很优雅,但如果可以的话......

    让我们用慢速服务器和不耐烦的客户端来验证:

    # Impatient Client
    use IO::Socket::INET;
    
    $sock = new IO::Socket::INET(
        PeerAddr => "localhost",
        PeerPort => "10007",
        Proto => 'tcp',
        Timeout => 2,
        );  
    
    print <$sock>;
    
    close($sock);
    
    
    # SlowServer
    use IO::Socket::INET;
    
    $sock = new IO::Socket::INET(
        LocalAddr => "localhost",
        LocalPort => "10007",
        Proto => 'tcp',
        Listen => 1,
        Reuse => 1,
        );
    
    $newsock = $sock->accept();
    sleep 5;
    
    #while (<$newsock>) {
    #    print $_;
    #}
    print $newsock "Some Stuff";
    close($newsock);
    close($sock);
    

    如果我们运行这个:

    pti@pti-laptop:~/playpen$ perl server.pl&
    [1] 9130
    pti@pti-laptop:~/playpen$ time perl test.pl
    Some Stuff[1]+  Done                    perl server.pl
    
    real    0m5.039s
    user    0m0.050s
    sys     0m0.030s
    

    因此它会忽略 2 秒超时并运行整整 5 秒。

    现在是另一个不耐烦的客户:

    use IO::Socket::INET;
    eval {
      local $SIG{ALRM} = sub { die 'Timed Out'; };
      alarm 2;
      $sock = new IO::Socket::INET(
        PeerAddr => "localhost",
        PeerPort => "10007",
        Proto => 'tcp',
        Timeout => 2,
        );
    
      print <$sock>;
    
      close($sock);
      alarm 0;
    };
    alarm 0; # race condition protection 
    print "Error: timeout." if ( $@ && $@ =~ /Timed Out/ );
    print "Error: Eval corrupted: $@" if $@;
    

    ~

    并运行它:

    pti@pti-laptop:~/playpen$ perl server.pl&
    [1] 9175
    pti@pti-laptop:~/playpen$ time perl test2.pl
    Error: timeout.Error: Eval corrupted: Timed Out at test2.pl line 3.
    
    real    0m2.040s
    user    0m0.020s
    sys         0m0.010s
    

    是的,这在 2 秒后按预期超时。

    【讨论】:

    • Timeout 属性没有被忽略,而是在IO::Socket 的构造函数中使用,而不是IO::Socket::INET。不过,这是一个很好的解决方法,对于 Windows,这样的事情是必要的。
    • 是的,但是在IO::Socket::INET的configure sub中注释掉了它的使用。 AFAICS 已设置,但从未在 ::INET 中使用。
    • 谢谢,这对我有用。但是,为什么在源代码中注释了那部分?
    • 请注意,给出的示例中的第二个 alarm 0 调用并不是“竞争条件保护”本身。相反,这种技术(即eval { alarm $n; ...; alarm 0; }; alarm 0)旨在从die()s 的代码中恢复,而不是用户安装的ALRM 处理程序,这将退出eval{} 块而不取消挂起的警报。 (当然,这种情况下的恢复本身就是活泼的,但那是另一回事。)
    • 问题中的代码对我有用。也许他们修好了。
    【解决方案2】:

    使用 IO::Socket::超时

    如下所示,它就像一个魅力。

    use IO::Socket::Timeout;
    my $socket = IO::Socket::INET->new( Timeout => 2 );
    IO::Socket::Timeout->enable_timeouts_on($socket);
    $socket->read_timeout(0.5);    # These will work
    $socket->write_timeout(0.5);   # These will work
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 2012-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-26
      • 1970-01-01
      相关资源
      最近更新 更多