Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm using IO::Socket right now to do some non-blocking network IO for a graphical application. IO::Socket has a blocking() method that purports to toggle blocking mode. It seems to work for reading and writing, but its connect() method--which gets called when you do IO::Socket::INET->new()--does not. First it calls gethostbyname(), which blocks, then it blocks using IO::Select to timeout the connect call. I don't want to rewrite the whole thing to use POE. Is there any way to do a non-blocking connect and gethostbyname() using IO::Socket? If not, what would be the least obtrusive alternatives?

  • Comment on Non-blocking connect() and gethostbyname()?

Replies are listed 'Best First'.
Re: Non-blocking connect() and gethostbyname()?
by Thelonius (Priest) on Oct 28, 2006 at 14:38 UTC
    For all this, I'm assuming that you're using a big select() to wait for all I/O and that you're not doing threads. (Threads would be one solution to all this, if they're supported on your perl/OS).

    About gethostname():
    I don't see any nonblocking DNS library on CPAN, so I think what you would have to do is create a pipe (or socket pair), fork, do the lookup in the child process and return the result via the pipe (you add the pipe to your select in the parent).

    About connect():
    Several possibilities:

    1. You don't have to use IO::Socket. You can use the plain socket functions, as documented in perlfunc. You can use the sockets returned by the socket() call in IO::Select->add or you can use plain select(), managing the bit vectors yourself (yuck).
    2. If you want an IO::Socket::INET object, you can create the socket with IO::Socket::INET new without a PeerAddr, then do the connect yourself. E.g.
      my $sock = IO::Socket::INET->new(Proto => 'tcp', Blocking => 0) or die "Socket error: $!\n"; my $iaddr = inet_aton($remote) || die "no host: $remote"; my $paddr = sockaddr_in($port, $iaddr); my $ret = connect($sock, $paddr); if (!$ret && ! $!{EINPROGRESS}) { die "Connect error: $!\n"; } # then select(), etc.
    3. Or, you could subclass IO::Socket::INET and override the connect method
      use strict; { package NBCSocket; use IO::Socket::INET; use Errno; use Carp; our @ISA = 'IO::Socket::INET'; sub connect { @_ == 2 or croak 'usage: $sock->connect(NAME)'; my $sock = shift; my $addr = shift; my $timeout = ${*$sock}{'io_socket_timeout'}; my $err; my $blocking; $blocking = $sock->blocking(0); if (!connect($sock, $addr)) { if (!$!{EINPROGRESS}) { $err = $!; $@ = "connect: $!"; } } $! = $err if $err; $err ? undef : $sock; } } use IO::Select; use Socket; $| = 1; my $address = shift || die "usage: $0 host:port\n"; $! = 0; my $err; print "Start: ", scalar(localtime), "\n"; my $sock = NBCSocket->new(PeerAddr => $address) or die "Socket error: $!\n"; print "after NBCSocket->new \$! = $!\n"; # of course, you'll probably want to do a select with other sockets $! = 0; my $select = IO::Select->new($sock); if ($select->can_write(5)) { my $peername = $sock->peername; if ($peername) { print "Connected: ", scalar(localtime), ", peername=$peername\n" +; } else { print "Not peername err = $!: ", scalar(localtime), "\n"; } } else { $err = $!; $sock_err = $sock->sockopt(SO_ERROR); print "Did not connect: ", scalar(localtime), "\n"; print "\$!=$err sock_err = $sock_err\n"; }

      I've never used it, but wouldn't Net::ADNS do the trick for asynchronous DNS lookups?

Re: Non-blocking connect() and gethostbyname()?
by dk (Chaplain) on Oct 30, 2006 at 13:37 UTC
    POE::Component::Client::DNS might be what you need, if you don't mind mating POE with your graphical application event loop.