From: "mitchell" <ffsnoopy / gmail.com>
>
> Well Bill, if you can write some wrappers for gets and puts to get the
> windows version working, I'd love that! I'm quite new to sockets and
> networking stuff in general, I just took a crash course on the basics.
> I also looked into this "nonblocking" thing but have no idea how it's
> used, or even if it works fully in windows now.

Hi mitchell,

As it happens windows ruby didn't support nonblocking I/O in 1.8.2
and prior anyway.  I haven't personally tried 1.8.4 on windows yet,
but I believe nonblocking socket I/O is finally supported (!!!!!!)
for which I am/will-be very grateful.  :)

However, the following methods should work as nonblocking
replacements for gets/puts on windows, even under 1.8.2.

I've tested them on ruby 1.8.2 (2004-12-25) [i386-mswin32] and
they appear to work.  (Apologies if there are any bugs.)

Hope this helps,

Regards,

Bill


require 'socket'

class TCPSocket

  # Nonblocking puts.  
  # Since 1.8.2 on win32 doesn't support nonblocking socket I/O,
  # we'll select on each character we send().
  #
  alias_method :orig_puts, :puts
  def puts(str)
    str = "#{str}\n" unless str[-1] == ?\n
    str.each_byte do |val|
      Kernel.select(nil, [self], nil, nil)
      self.send(val.chr, 0)
    end
  end

  # Buffered nonblocking gets.  Returns partial last line if any.
  # Returns nil when eof reached.
  # 
  alias_method :orig_gets, :gets
  def gets
    buf = (@nb_gets_buf ||= "")
    while (lineparse = buf.split(/\n/, 2)).length < 2
      Kernel.select([self], nil, nil, nil)
      dat = self.recv(65536)  # arbitrary read length
      if dat.empty?  # reached eof?
        @nb_gets_buf = nil
        return buf.empty? ? nil : buf  # return partial last line if we have one
      end
      buf << dat
    end
    buf.replace(lineparse[1])  # store remaining unparsed data back in buffer
    lineparse[0] + "\n"
  end
end



# here's the code I tested it with... i should have made them automated
# tests i guess - these require visual inspection of the output....
# it prints a big ugly mess of stuff to the console...  ;-/

require 'thread'
$stdout.sync = true

sv = TCPServer.new(12345)
cl = TCPSocket.new('localhost', 12345)
sv_cl = sv.accept

happy_th = Thread.new { loop { puts "happy!"; sleep 1 } }

gobble_th = Thread.new { 
  while line = sv_cl.gets
    print "gobble!(#{line.inspect}) "
    sleep(0.1) if rand > 0.99  # make sure we fall behind the main puts() thread
  end
}

0.upto(12288) do |i|
  print "#{i} "
  cl.puts(i.to_s)
end
cl.send("p", 0)  # end with a "partial" line

cl.close
gobble_th.join

# if the final gobbles you see in the console before the program
# exits are:
#   gobble!("12288\n") gobble!("p")
# ... then it "worked".