From: Bill Kelly Date: 2006-05-06T13:42:42+09:00 Subject: Re: Considering Ruby For a Networking Application From: "James Edward Gray II" > > I'm going to be working on a fairly basic networking application > shortly. This is low level socket work. (I cannot use a nicety like > DRB sadly.) > > I'm very familiar with this kind of stuff in Perl, but have done very > little socket work in Ruby. I need something that can reliably work > with around 50 users sending light activity. There is the > possibility of a large write to them here and there and that cannot > block the process. > > I know a little from past questions here: > > http://rubyurl.com/miq > > However, I think I remember someone saying Ruby 1.8.4 is better with > nonblocking sockets. So, can some networking guru take a stab at > these questions: > > 1. I need to use threads *and* nonblocking sockets to be safe, right? > 2. Can I use gets() and puts() now in Ruby 1.8.4 or do I still need > to do low level reads and writes? > 3. Do I need to rescue Errno::EWOULDBLOCK? > 4. This isn't going to work on Windows, right? (That's okay in this > case. Just making sure.) I'm sorry I don't have definitive answers. I'm going to need to find out for sure pretty soon, myself. But . . . . From looking at the changes in the ruby sources between 1.8.2 and 1.8.4, it _looks_ as though nonblocking sockets are pretty well supported now. (Even on Windows!) It would appear nonblocking is handled properly internally whether you use ruby threads or not. (I.e. from the perspective of the underlying mechanisms, the main ruby thread is just as much a thread as any other ruby thread.) In 1.8.4, you can: require 'io/nonblock' This adds methods to the IO class, allowing: some_socket.nonblock = true In the past, I've done all my ruby socket programming using the lower level send() and recv() methods. However, again from reading the source (io.c and ext/openssl/lib/openssl/buffering.rb) it looks like IO#readpartial or IO#sysread (but *not* IO#read) may be the new favored approach to dealing with nonblocking descriptors in general. (In Windows, I'm pretty sure only sockets have any chance of working with nonblocking semantics. Whereas in Unix, I'd presume support for any file descriptor. So using IO#sysread and IO#syswrite, now that these methods apparently are nonblocking-savvy, should be more general than using send() and recv().) If you look at ext/openssl/lib/openssl/buffering.rb, you'll see what looks like a fairly generic module for building a buffered read and readpartial, as well as write, on top of sysread and syswrite. I note that the lowest level read and write methods here both rescue Errno::EAGAIN, and retry. I'm a little surprised by that - because it appears that EAGAIN is already being handled along with EWOULDBLOCK in io.c anyway. But I haven't really studied the code in io.c, just perused it. Hmm.......... My sense of it--at least, this is what I plan to try in my first test--is: 1. require 'io/nonblock' and set 'nonblock = true' on all my sockets. 2. use a separate ruby thread to handle I/O for each socket. 3. use IO#readpartial to read... (note that the comments in io.c say that IO#readpartial can block regardless of the nonblocking flag... but I'm pretty sure this means block the ruby thread, NOT the native thread...!) 4. use IO#syswrite to write... (I think, with 1.8.4 and a nonblocking socket, ruby _appears_ to be handling EWOULDBLOCK internally, so that we can do a large write but not block the whole process.) - But maybe we need to rescue Errno::EAGAIN for some reason, as seen in openssl/buffering.rb With luck, this will work on Unix and Windows (sockets only on windows, any file descriptor on unix), and never block the whole native thread ruby runs in. If so, we've got a reasonable solution for TCP. UDP had its own unique blocking issues (related to linux kernels breaking POSIX semantics for select/recv for packets with bad UDP checksums.... or something like that.... I dunno if this has changed since 1.8.2 or not...) Or, yeah, maybe that EventMachine library... Regards, Bill