views:

1986

answers:

3

I'm reading lines of input on a TCP socket, similar to this:

class Bla  
  def getcmd
    @sock.gets unless @sock.closed?
  end

  def start     
    srv = TCPServer.new(5000)
    @sock = srv.accept
    while ! @sock.closed?
      ans = getcmd
    end
  end
end

If the endpoint terminates the connection while getline() is running then gets() hangs.

How can I work around this? Is it necessary to do non-blocking or timed I/O?

A: 

If you believe the rdoc for ruby sockets, they don't implement gets. This leads me to believe gets is being provided by a higher level of abstraction (maybe the IO libraries?) and probably isn't aware of socket-specific things like 'connection closed.'

Try using recvfrom instead of gets

Orion Edwards
+3  A: 

You can use select to see whether you can safely gets from the socket, see following implementation of a TCPServer using this technique.

require 'socket'

host, port = 'localhost', 7000

TCPServer.open(host, port) do |server|
  while client = server.accept
    readfds = true
    got = nil
    begin
      readfds, writefds, exceptfds = select([client], nil, nil, 0.1)
      p :r => readfds, :w => writefds, :e => exceptfds

      if readfds
        got = client.gets 
        p got
      end
    end while got
  end
end

And here a client that tries to break the server:

require 'socket'

host, port = 'localhost', 7000

TCPSocket.open(host, port) do |socket|
  socket.puts "Hey there"
  socket.write 'he'
  socket.flush
  socket.close
end
manveru
Small typo in there, I believe you wanted: http://gist.github.com/527750
rogerdpack
+1  A: 

The IO#closed? returns true when both reader and writer are closed. In your case, the @sock.gets returns nil, and then you call the getcmd again, and this runs in a never ending loop. You can either use select, or close the socket when gets returns nil.

Roman
If the socket is closed the gets will hang
JustSmith
yeah if you add a print 'here'statement within your getcmd loop you'll see that it's looping forever, reading ""
rogerdpack