Forum: Ruby How to do INTERESTING things with Ruby sockets???

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
219fcd6acd2770ee252cc4c08aa13bf6?d=identicon&s=25 theosib@gmail.com (Guest)
on 2007-03-07 02:36
(Received via mailing list)
I've done quite a bit of reading about socket programming in Ruby, but
most of the examples show simple cases of accepting a socket
connection, spitting out some fixed bit of data, and then closing.
What about socket connections where you can't predict the length or
timing of data that you'll receive?  None of the available web pages
give any hint of how to work interactively with sockets.

Here are a few things that I was hoping maybe some knowledgable people
could help me figure out how to do with sockets in Ruby:

- Block until 1 or more bytes have been received, and then return as
many as have arrived (even if fewer than some buffer size)

- Block until a fixed number of bytes have been received, or a time-
out has elapsed

- Check how many bytes have been received without blocking

- Detect when the other end has closed the socket

- Distinguish between a timeout and a closed socket


Thank you for your help!
481b8eedcc884289756246e12d1869c1?d=identicon&s=25 Francis Cianfrocca (blackhedd)
on 2007-03-07 02:50
(Received via mailing list)
On 3/6/07, theosib@gmail.com <theosib@gmail.com> wrote:
>
> - Distinguish between a timeout and a closed socket
You don't say if this is a learning exercise in low-level socket
programming
or if you actually have to develop an app. If it's the latter, you may
want
to look at the Ruby/EventMachine library. It wraps up the low-level
socket
issues so you can concentrate on the app-level code.
B351db35040dcc3be001dd51ba798a95?d=identicon&s=25 unknown (Guest)
on 2007-03-07 20:10
(Received via mailing list)
In article <1173231146.477057.42960@q40g2000cwq.googlegroups.com>,
"theosib@gmail.com" <theosib@gmail.com> writes:
> I've done quite a bit of reading about socket programming in Ruby, but
> most of the examples show simple cases of accepting a socket
> connection, spitting out some fixed bit of data, and then closing.
> What about socket connections where you can't predict the length or
> timing of data that you'll receive?  None of the available web pages
> give any hint of how to work interactively with sockets.

To do the things that you seem to want to do, you will need to
program to the low-level "Berkeley" socket interface.  This is the
interface adopted by both the UNIX/POSIX/Linux and Microsoft WINSOCK
specifications, with slight alterations, of course.  Ruby exposes these
through the 'socket' library.

> Here are a few things that I was hoping maybe some knowledgable people
> could help me figure out how to do with sockets in Ruby:
>
> - Block until 1 or more bytes have been received, and then return as
> many as have arrived (even if fewer than some buffer size)

This is the default behavior of the socket API.

> - Block until a fixed number of bytes have been received, or a time-
> out has elapsed

To wait for a "full" buffer, you can use the MSG_WAITALL flag to
recv* to wait for a full buffer.  A timeout would be effected by
using SO_RCVTIMEO at the underlying API, but that doesn't work
under Ruby due to a deficiency in the user-mode threads implementation.

In any case, the underlying APIs do not let you mix the two
concepts.  If a timeout were to occur, a partial buffer would be
returned if *any* data were available.  There is no way to eliminate
the possibility of a partial read while still enabling a timeout.

> - Check how many bytes have been received without blocking

There is no portable way to do this, and I recommend strongly
against it.  However, if you must, most UNIX-inspired and WINSOCK
systems support the FIONBIO ioctl.  Most implementations try to
give you an estimate of the number of octets that are available,
but this estimate is often low (and on some systems is simply
zero/one).

> - Detect when the other end has closed the socket

The only way to to do this portably (or easily) is to read the
available data.  When you read zero bytes, you have read all the
available data, and the connection has been closed.  There is no
portable way to determine the reason (possible causes include at
least a remote close, a remote reset, and a force close initiated
by the local host).


> - Distinguish between a timeout and a closed socket

I'm presuming that you mean here a timeout on a read call and not
the local host closing the socket.  In that case, I don't know of
any way to do that straight-forwardly in Ruby in a single call,
so I'd recommend the use of select with a non-blocking socket.  I
attach an example below. You should carefully read the Ruby and
platform documentation on fcntl and select.

A few notes on the example below:

- It implements a client reader for RFC 868 (Time Protocol).  This
  was an experiment in using Ruby and I've only exercised this
  program on UNIX/POSIX/Linux systems. I have exercised analogous,
  but non-Ruby programs, under WINSOCK.

- The use of non-blocking sockets is not mandatory.  The program
  should generally work without setting this mode, but there then
  might be a few rare cases where the program pauses inexplicably
  for indeterminate periods of time.

----------------------------------------------------------------------------
#! /usr/bin/env ruby

# rb_getnettime3 - an alternate form that uses a select call to effect
# a timeout when polling unresponsive hosts.

require 'socket'
require 'fcntl'

hostname = (ARGV.length > 0) ? ARGV[0] : '127.0.0.1'

timeport = Socket.getservbyname('time', 'udp')  # port 37
RFC868_POSIX_ADJMENT = 2_208_988_800    # diff between RFC 868 and POSIX

tsecs = 5

rmttime = 0
begin
        rcvd = nil
        UDPSocket.open { |sock|
                origflags = sock.fcntl(Fcntl::F_GETFL, 0)
                sock.fcntl(Fcntl::F_SETFL, origflags |
Fcntl::O_NONBLOCK)

                sock.send('', 0, hostname, timeport)

                IO.select([sock], [], [], tsecs) or raise 'receive time
out'

                rcvd = sock.recvfrom(4, 0)      # read in one 'N'
        }
        raise 'Bad format in response' unless rcvd.length == 2

        data, rmthost = rcvd
        raise 'Invalid response from remote host' unless data.length ==
4

        rmttime = data.unpack('N')[0]
rescue RuntimeError => msg
        abort "Call to time port failed:  #{msg}"
end

adjtime = rmttime - RFC868_POSIX_ADJMENT
puts "Remote time: #{Time.at(adjtime)}"
This topic is locked and can not be replied to.