Recovering from a broken TCP socket in Ruby when in gets()

前端 未结 5 783
花落未央
花落未央 2021-01-05 08:40

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 =          


        
相关标签:
5条回答
  • 2021-01-05 08:41

    I recommend using readpartial to read from your socket and also catching peer resets:

    while true
        sockets_ready = select(@sockets, nil, nil, nil)
        if sockets_ready != nil
          sockets_ready[0].each do |socket|
            begin
              if (socket == @server_socket)
                # puts "Connection accepted!"
                @sockets << @server_socket.accept
              else
                # Received something on a client socket
                if socket.eof?
                  # puts "Disconnect!"
                  socket.close
                  @sockets.delete(socket)
                else
                  data = ""
                  recv_length = 256
                  while (tmp = socket.readpartial(recv_length))
                    data += tmp
                    break if (!socket.ready?)
                  end
                  listen socket, data
                end
              end
            rescue Exception => exception
              case exception
                when Errno::ECONNRESET,Errno::ECONNABORTED,Errno::ETIMEDOUT
                  # puts "Socket: #{exception.class}"
                  @sockets.delete(socket)
                else
                  raise exception
              end
            end
          end
        end
      end
    

    This code borrows heavily from some nice IBM code by M. Tim Jones. Note that @server_socket is initialized by:

    @server_socket = TCPServer.open(port)
    

    @sockets is just an array of sockets.

    0 讨论(0)
  • 2021-01-05 08:49

    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.

    0 讨论(0)
  • 2021-01-05 08:52

    I simply pgrep "ruby" to find the pid, and kill -9 the pid and restart.

    0 讨论(0)
  • 2021-01-05 08:54

    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
    
    0 讨论(0)
  • 2021-01-05 08:59

    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

    0 讨论(0)
提交回复
热议问题