Forum: Ruby How to set socket option SO_RCVTIMEO

Posted by Zakaria (Guest)
on 2006-11-30 09:51
(Received via mailing list)
Hi,

How to set socket option SO_RCVTIMEO in Windows XP?
The C version use somekind struct.

Regards,


-- Zakaria
   z4k4ri4@gmail.com             Yahoo!: z4k4ri4
Posted by Kem Mason (bulbous)
on 2008-11-13 19:11
Did you ever find out how to do this?  I am looking around, haven't 
found any documentation

Zakaria wrote:
> Hi,
> 
> How to set socket option SO_RCVTIMEO in Windows XP?
> The C version use somekind struct.
> 
> Regards,
> 
> 
> -- Zakaria
>    z4k4ri4@gmail.com             Yahoo!: z4k4ri4
Posted by Brian Candler (candlerb)
on 2008-11-13 22:23
Zakaria wrote:
> How to set socket option SO_RCVTIMEO in Windows XP?
> The C version use somekind struct.

First, read the option:

irb(main):001:0> require 'socket'
=> true
irb(main):002:0> s = TCPServer.new(1234)
=> #<TCPServer:0xb7d246d4>
irb(main):003:0> s.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO)
=> "\000\000\000\000\000\000\000\000"

So if it's a struct timeval, that's a 32-bit tv_sec followed by a 32-bit 
tv_usec. Not clear about the byte order, though, since it's all zero 
here.

So let's try native byte ordering, to set a timeout of 1.5 seconds (1 
second + 500,000 microseconds):

irb(main):008:0> n = [1, 500_000].pack("I_2")
=> "\001\000\000\000 \241\a\000"
irb(main):009:0> s.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, n 
)
=> 0
irb(main):010:0> s.accept
Errno::EAGAIN: Resource temporarily unavailable
  from (irb):10:in `accept'
  from (irb):10
  from :0

Yep, there was a pause of 1.5 seconds before the exception was raised.

Look OK?
Posted by Kem Mason (bulbous)
on 2008-11-13 22:39
I tried something like that, and it never timed out -- so after your 
post, I tried the following (the server I'm connecting to reads a CRLF 
terminated string, and returns a response after X seconds (150 in this 
case))

sock = TCPSocket.new('foodlinux', 3232)
=> #<TCPSocket:0x2be080>
opt = [1, 1].pack("I_2")
=> "\001\000\000\000\001\000\000\000"
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, opt)
=> 0
sock.write("150\r\n")
=> 5
puts "read: #{sock.gets}"
....

it never times out -- seems like it should return after 1.000001 seconds 
regardless of the byte ordering.

What am I missing here?





Brian Candler wrote:
> Zakaria wrote:
>> How to set socket option SO_RCVTIMEO in Windows XP?
>> The C version use somekind struct.
> 
> First, read the option:
> 
> irb(main):001:0> require 'socket'
> => true
> irb(main):002:0> s = TCPServer.new(1234)
> => #<TCPServer:0xb7d246d4>
> irb(main):003:0> s.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO)
> => "\000\000\000\000\000\000\000\000"
> 
> So if it's a struct timeval, that's a 32-bit tv_sec followed by a 32-bit 
> tv_usec. Not clear about the byte order, though, since it's all zero 
> here.
> 
> So let's try native byte ordering, to set a timeout of 1.5 seconds (1 
> second + 500,000 microseconds):
> 
> irb(main):008:0> n = [1, 500_000].pack("I_2")
> => "\001\000\000\000 \241\a\000"
> irb(main):009:0> s.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, n 
> )
> => 0
> irb(main):010:0> s.accept
> Errno::EAGAIN: Resource temporarily unavailable
>   from (irb):10:in `accept'
>   from (irb):10
>   from :0
> 
> Yep, there was a pause of 1.5 seconds before the exception was raised.
> 
> Look OK?
Posted by Kem Mason (bulbous)
on 2008-11-14 00:32
It appears that I am supposed to call setsockopt before connecting -- 
which would explain why your example (which calls setsockopt before 
calling accept()) works, and mine, where it is client side, does not.  I 
have yet to figure out how to create a client socket that I can call 
setsockopt on without connecting right away.

I'll post any update
Posted by Patrick Doyle (Guest)
on 2008-11-14 00:51
(Received via mailing list)
How about something like:

s = Socket.new(...)
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, ...)
s.connect(...)

--wpd
Posted by Kem Mason (bulbous)
on 2008-11-14 00:53
hehe yeh I just realized I could use a regular Socket instead of a TCP 
Socket -- it doesn't appear to be the only problem though.  here's my 
latest code (which still doesn't timeout properly)


require 'socket'

opt = [1, 500_000].pack("I_2")

sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
sockaddr = Socket.pack_sockaddr_in(3232, '192.168.1.10')

sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, opt)
puts "sockopt = #{sock.getsockopt(Socket::SOL_SOCKET, 
Socket::SO_RCVTIMEO).inspect}"

sock.connect(sockaddr)

#this tells the server at 192.168.1.10:3232 to wait 10 seconds before 
responding, so we should get a timeout on our socket
sock.write("10\r\n")

start_time = Time.now
result = sock.gets
time = Time.now - start_time

puts "read: #{sock.gets} after: #{time} seconds"
#no timeout occured, this read returns after 10 seconds




Patrick Doyle wrote:
> How about something like:
> 
> s = Socket.new(...)
> s.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, ...)
> s.connect(...)
> 
> --wpd
Posted by Brian Candler (candlerb)
on 2008-11-14 09:07
Kem Mason wrote:
> hehe yeh I just realized I could use a regular Socket instead of a TCP 
> Socket -- it doesn't appear to be the only problem though.  here's my 
> latest code (which still doesn't timeout properly)

Well, probably you shouldn't be using setsockopt for this at all. Note 
that setting SO_RCVTIMEO just raises an EAGAIN on timeout; I'd expect 
that gets would trap that and retry.

Why not simply:

require 'timeout'
result = nil
timeout(1.5) {
  result = sock.gets
}
Posted by Brian Candler (candlerb)
on 2008-11-14 09:10
Also, if you're not concerned about the possibility of transmission 
stopping part way through a response, you can do

if select([sock],nil,nil,1.5)
  result = sock.gets
else
  puts "I timed out!"
end
Posted by Kem Mason (bulbous)
on 2008-11-14 17:25
The problem with using timeout is that it is unsafe: (see 
http://tinyurl.com/5fngha )
I have actually encountered a problem using timeout where my thread hung 
forever (10 times in production over the last couple months) -- so this 
is why I am starting to do something else.

You're right that gets silently ignores the exception -- turns out that 
was the main problem -- if I use sock.recvfrom, I get the exception as I 
want -- then I just have to handle the case where I get only part of the 
response (and still have time left).

All is working now when using the code at: http://pastie.org/314914

thanks much for your help all


Brian Candler wrote:
> Kem Mason wrote:
>> hehe yeh I just realized I could use a regular Socket instead of a TCP 
>> Socket -- it doesn't appear to be the only problem though.  here's my 
>> latest code (which still doesn't timeout properly)
> 
> Well, probably you shouldn't be using setsockopt for this at all. Note 
> that setting SO_RCVTIMEO just raises an EAGAIN on timeout; I'd expect 
> that gets would trap that and retry.
> 
> Why not simply:
> 
> require 'timeout'
> result = nil
> timeout(1.5) {
>   result = sock.gets
> }
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.