If I try like this:
my $sock = IO::Socket::INET->new( … ) or die "no socket for you";
defined $sock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, 30) or die "setsockopt: $!";
then my script suffers death from "setsockopt: Invalid argument at [line 2]". The IO::Socket
and perlfunc
pods do not say, though perlfunc gives an example with TCP_NODELAY
which makes it look like the above should work.
(quick note: I've answered my own question, as best I can, but certainly welcome a better answer. The most obvious "better" would be for it to be portable, at least on POSIX machines)
By using strace
on the Perl interpreter running the script, it becomes clear that the problem is that Perl isn't packing a struct timeval
(required by SO_RCVTIMEO
) for you. Further, there do not appear to be helper functions to do it for you. Instead, you must do it yourself.
This turns out to be problematic because struct timeval
is machine-specific. The Single Unix Specification defines it:
The <sys/time.h> header shall define the timeval structure, which shall include at least the following members:
time_t tv_sec Seconds. suseconds_t tv_usec Microseconds.
It also says that time_t
is an integer or real-floating type, and "suseconds_t
shall be a signed integer type capable of storing values at least in the range [-1, 1000000]" (see sys/types.h).
Without access to the C structure, it isn't possible to do this portably. But if we assume glibc, that has a more restrictive definition, specifying both as long
, and that they're the only two members. However, this is a documentation bug. So never mind.
So, the best I can do, which I believe works on both GNU/Linux IA-32 and GNU/Linux AMD64, is this:
$sock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 30, 0))
or die "setsockopt: $!";
The pack
format l!
means to use the current machine's native long
—which is what the glibc docs say, and is apparently implemented for at least some glibc architectures (but not SPARC, according to the bug).