7 Stimmen

Das Wiederherstellen eines defekten TCP-Sockets in Ruby, wenn in gets().

Ich lese Zeilen von Eingabe auf einem TCP-Socket, ähnlich wie hier:

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

Wenn der Endpunkt die Verbindung beendet, während getline() läuft, hängt gets().

Wie kann ich das umgehen? Ist es notwendig, nicht-blockierende oder zeitgesteuerte E/A zu verwenden?

7voto

manveru Punkte 2600

Sie können select verwenden, um festzustellen, ob Sie sicher Daten vom Socket abrufen können. Im folgenden Beispiel wird die Implementierung eines TCPServers mithilfe dieser Technik gezeigt.

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

Und hier ein Client, der versucht, den Server zu unterbrechen:

require 'socket'

host, port = 'localhost', 7000

TCPSocket.open(host, port) do |socket|
  socket.puts "Hey there"
  socket.write 'he'
  socket.flush
  socket.close

2voto

Roman Punkte 12945

Das IO#closed? gibt true zurück, wenn sowohl der Reader als auch der Writer geschlossen sind. In Ihrem Fall gibt @sock.gets nil zurück, und dann rufen Sie getcmd erneut auf, was zu einer Endlosschleife führt. Sie können entweder select verwenden oder den Socket schließen, wenn gets nil zurückgibt.

0voto

Ben Flynn Punkte 17854

Ich empfehle, readpartial zu verwenden, um von Ihrem Socket zu lesen und auch Peer-Resets abzufangen:

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
            # Etwas auf einem Client-Socket empfangen
            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

Dieser Code lehnt sich stark an einige schönen IBM-Code von M. Tim Jones an. Beachten Sie, dass @server_socket initialisiert wird mit:

@server_socket = TCPServer.open(port)

@sockets ist einfach ein Array von Sockets.

0voto

wakeupbuddy Punkte 771

Ich führe einfach pgrep "ruby" aus, um die PID zu finden, und töte die PID mit -9 und starte sie neu.

-2voto

Orion Edwards Punkte 117361

Wenn Sie der rdoc für Ruby-Sockets glauben, implementieren sie wahrscheinlich kein gets. Dies lässt mich glauben, dass gets von einer höheren Abstraktionsebene bereitgestellt wird (vielleicht von den IO-Bibliotheken?) und wahrscheinlich nicht über dinge wie 'Verbindung geschlossen' von Sockets informiert ist.

Versuchen Sie stattdessen recvfrom anstelle von gets

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X