views:

496

answers:

3

I'm using perl (which hopefully shouldn't affect anything), but I need to know how I can set a timeout for the connect operation. The problem is I can't wait forever for the connect operation to happen. If it doesn't happen within a few seconds, I'd rather give-up and move on.

socket(my $sock, PF_INET, SOCK_STREAM, (getprotobyname('tcp'))[2]);
setsockopt($sock, SOL_SOCKET, SO_SNDTIMEO, 10); # send timeout

print "connecting...\n";
connect($sock, sockaddr_in(80,scalar gethostbyname('lossy.host.com')));
print "connected...\n";

The problem is, if the connection to "lossy.host.com" is "lossy" or slow or anything but fast, I'd rather give up than make the user wait. (Think of it as a side-effect to a program that does something else... the user probably doesn't expect this script to communicate with a server somewhere...).

Threading Case: How would you interrupt the connect()? Would you just detach the thread and forget about it?

A: 

You could spawn a separate thread to do it, and then do a timed wait for a result. If you don't receive a result in an appropriate amount of time, give up waiting and just let the thread continue. It will eventually time out, or you might be able to kill the thread.

To answer the initial question, I don't think there's a way to change the connect() timeout, at least not through a sockets API. On Windows, I wouldn't be surprised if there's a registry key you could change that would affect it, but I don't know what it would be.

Graeme Perrow
saddly, that may be what I will have to do on windows, but that makes me sad...
xyld
Ah, your edit makes things a little clearer. Thanks.
xyld
+2  A: 

You can use fcntl to set the socket to be non-blocking, then select with a timeout waiting for it to become readable. If it doesn't become readable before the timeout, you could close it at that point.

I know how to do this in C, but not perl, otherwise I'd give you an example. The perlfunc manpage says that all of these functions exist and a cursory read seems to say they'll work like you want.

Edit: sorry, missed the part where perlfunc says they may not be available on non-Unix systems, and indeed, fcntl isn't available on win32. There is an IO::Socket library that you can use that will do the right thing on Windows though.

Here's sample code that works for me (on linux anyway):

#!/usr/bin/perl

use IO::Socket::INET;
use IO::Select;

$sock = IO::Socket::INET->new('PeerAddr' => 'lossy.host.com',
                              'PeerPort' => 80,
                              'Blocking' => 0 );

$sel = IO::Select->new( $sock );

@writes = $sel->can_write(10);

if ( $sock->connected ) {
    print "socket is connected\n";
} else {
    print "socket not connected after however long\n";
    $sock->close;
}
Eric Warmenhoven
Can you be more specific? How would you do this using the IO::Socket library? Set the socket non-blocking?
xyld
That's right, when you construct the socket, set 'Blocking' to 0 (i.e. non-blocking). It should use a non-blocking connect then.
Eric Warmenhoven
I tried this, for some reason I can't get it to work correctly on my windows box... Have you actually tried this on windows?
xyld
I only tested it on Linux. Unfortunately I don't have a windows box to test with, otherwise I would have. Though, looking through the docs, it looks like it should work. Do you know where it's failing?
Eric Warmenhoven
A: 

If you end up doing the threaded case wherein you detach the connecting thread without killing it, beware the following: Windows only lets you have a maximum of 10 pending outgoing TCP connections (the 11th will block until one of the pending ones times out).

This was the cause of much frustration for me. I think MS put this in to prevent botnets from spreading or something. I don't think there's any way to switch it off either.

Seth
Sounds like a "windows thing to do". Thanks though! I'll keep that in the back of my brain with all the other windows odds and ends ;)
xyld