James,
Looks like this has already been explained from the Ruby perspective,
but thought I'd give you what I know of the "official" information. All
of this comes from Effective TCP/IP Programming by Jon C. Snader, which
is probably the absolute best book on TCP/IP programming ever written.
Go get it or I will hunt you down and paint you up like a clown.
* You should always get the original fcntl settings and reuse them in
the set operation. Many operating systems use fcntl settings in weird
ways.
* You can set O_NONBLOCK on a socket during connect when you want a
client to attempt a connect, and then go back into your select loop to
wait for the response. Also useful as a connect timeout. Add a timeout
to your select and then it will return if the connect..well..times out.
This has the following requirements though:
- If the connection is not established during the connect call, then it
will return an EINPROGRESS error. Not sure what Ruby does with this.
- If the connect is so fast that it happens immediately then you'll get
a 0 (success) return code. You need to check for this after the connect
or else your select will not work. Skip the select if this happens as
you're already connected.
- After the connect comes back you'll need to use your previously saved
fcntl flags to reset it if you don't want nonblocking anymore.
* Nonblocking accept is usually not needed if you're doing a select/poll
style loop. The exception to this is if you have so much processing
that you don't get around to the accept in time to avoid dropping
clients. In this case either re-write your client handlers to not eat
up so much time, or set the accept socket to nonblock so that you can do
multiple accepts in a loop.
* The practical rule on nonblocking accept is:
- An OS could return EWOULDBLOCK, ECONNABORTED, EINTR as a non-fatal
error which just means try again later.
- All other errno are considered bad and you should deal with them as
an error.
- The semantics are different for different platforms, and I believe
Windows is really weird. You'll need to test on as many as you can, but
I think if you handle those you'll be good.
* Select does have a problem with getting the read response right, which
means you almost always need to set the socket to non-blocking, but also
select only indicates that you *can* read/write, not how much. So, if
you need to read/write 1024 bytes, but the OS buffers only allow for 512
at that moment, then your call will block. Finding out how much you can
write is only possible on some OS with non-standard fcntl. It's just
easier to use nonblocking IO. I'm not sure if poll, /dev/poll, epoll,
or kqueue have a similar problem as select.
* The major problem with Windows vs. BSD sockets is that Windows uses a
special SOCKET type rather than an integer file descriptor. Snader's
book has a nice little wrapper around this, but I believe Ruby handles
this problem as well. Where you'll run into problems (as I am with the
libevent extension I'm working on) is when you pass these SOCKETs to an
external library expecting an integer. Still haven't figured that one
out.
Anyway, enjoy.
Zed A. Shaw
On Sun, 2005-07-17 at 05:52 +0900, James Edward Gray II wrote: