Perl: How to get IO::Socket::INET timeout after X seconds?

前端 未结 2 1622
梦谈多话
梦谈多话 2021-01-02 17:57

I\'m trying to connect to some host, using invalid port, and i want to get timeout after X seconds. How to do that ?

My code:

 $sock = new IO::Socket         


        
相关标签:
2条回答
  • 2021-01-02 18:32

    If you check the code you'll see (I copied it from my 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;
    #        }
    

    Apparently the timeout stuff is commented out so that expleins why it is ignored.

    I found a post dating from 2003 where this was discussed. One suggestion (at the bottom) was to open the socket in an eval block which gets terminated by an alarm signal :

    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 $@; 
    

    Not very elegant, but if it works...

    Let's verify with a slow server and impatient client :

    # 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);
    

    if we run this:

    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
    

    So it ignores the 2 second timeout and runs for the full 5 seconds.

    Now the other impatient client :

    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 $@;
    

    ~

    and running it :

    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
    

    Yep, this timeouts after 2 seconds as expected.

    0 讨论(0)
  • 2021-01-02 18:38

    So much easier is to use the IO::Socket::Timeout

    as per below and it works like a charm.

    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
    
    0 讨论(0)
提交回复
热议问题