From: Robert Klemme Date: 2011-09-07T16:27:43+09:00 Subject: Re: TCPSocket recv's nil with gets() if the server crashes? Extending a bit on Eric's excellent reply: On Wed, Sep 7, 2011 at 8:19 AM, Bill W. wrote: > I don't claim to be much of a programmer, and I am also new to RUBY, so > hopefully this is a good question. > > I am making a client that can connect to a port and read an unknown > number of lines, like a server banner. > Since the server doesn't close the connection, the program will not move > on.  To overcome this, I used select() with a timeout, so it will read > whatever is in the pipe and move on if it gets nothing more within n > seconds. > > Everything worked fine, until I tried crashing the server.  I started to > get a string of nil coming into the client. > > Here is the code for the server: > > #!/usr/bin/ruby > > require 'socket'               # Get sockets from stdlib > > server = TCPServer.open(2000)  # Socket to listen on port 2000 > loop {                         # Servers run forever >  client = server.accept       # Wait for a client to connect >  client.puts(Time.now.ctime)  # Send the time to the client >  #Don't close the connection, to simulate an unknown size. If you do not switch the socket to sync=true or invoke #flush here chances are that the content is still sitting in the internal buffer (Ruby does buffered IO) when you kill the process. I suggest to add client.flush because that has the advantage of still buffering and explicit delimiting "messages" (which might consist of multiple lines). In other words you make the sending explicit. This is especially necessary in situations where there are pauses between the sending of different messages and you want the client to process messages rather sooner than later. You could try with require 'socket' # Get sockets from stdlib server = TCPServer.open(2000) # Socket to listen on port 2000 loop { # Servers run forever client = server.accept # Wait for a client to connect client.puts(Time.now.ctime) # Send the time to the client # uncomment to see the difference: # client.flush sleep 10 client.puts(Time.now.ctime) # Send the time to the client # uncomment to see the difference: # client.flush #Don't close the connection, to simulate an unknown size. } > } > > And my client: > > #!/usr/bin/ruby > > require 'socket' > > host = 'localhost' > port = 2000 > sockets = Array.new #select() requires an array > #fill the first index with a socket > sockets[0] = TCPSocket.open(host, port) > while 1                              #loop till it breaks > #listen for a read, timeout 10 seconds > res = select(sockets, nil, nil, 10) >  if res != nil  #a nil is a timeout and will break >        #THIS PRINTS NIL FOREVER on a server crash >    puts sockets[0].gets() >  else >    sockets[0].close >    break >  end > end > > If the server stays up, the client gets the time, waits 10 seconds for > more, and just quits and closes. > If you kill the server before select() times out, you will get an > infinite loop of 'nil' in the client. > I used socket[0] instead of a loop to parse read events in res since > there is only one socket, and it must be the one triggering select.  (I > checked to be sure, res[0] and socket[0] are the same socket) > > I could use this 'nil' to show that the server has crashed, but I can't > help but think it is a symptom of something I've done terribly wrong > that works for now but will bite me in the future. > > Any thoughts on where this 'nil' is from? See Eric's reply. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/