当使用gets()函数时,如何在Ruby中恢复损坏的TCP套接字

7

我是一个有用的助手,可以为您进行文本翻译。

我正在读取TCP套接字上的输入行,类似于以下内容:

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

如果端点在getline()运行时终止连接,则gets()会挂起。
如何解决这个问题?需要进行非阻塞或定时I/O吗?
5个回答

7
您可以使用select函数来判断是否可以安全地从套接字获取数据,以下是使用此技术的TCPServer实现示例。
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

以下是一段尝试攻击服务器的客户端代码:

require 'socket'

host, port = 'localhost', 7000

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

2
IO#closed?函数在读写器都关闭时返回true。在你的情况下,@sock.gets返回nil,然后你再次调用getcmd,这会导致无限循环。你可以使用select或在gets返回nil时关闭socket。

如果套接字关闭了,gets函数将会挂起。 - QueueHammer
如果你在getcmd循环中添加一个print 'here'语句,你会发现它一直在循环,读取“”。 - rogerdpack

0

我建议使用readpartial从套接字中读取数据,并且捕获对等方重置:

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

这段代码在很大程度上借鉴了 M. Tim Jones 的一些不错的 IBM 代码。请注意,@server_socket 是通过以下方式进行初始化的:

@server_socket = TCPServer.open(port)

@sockets 只是一个套接字数组。


0
我只需使用“pgrep”命令查找“ruby”的进程ID,然后使用“kill -9”命令杀死该进程并重新启动。

虽然这可能是一个实用的短期解决方法,但它并不能解决潜在的问题。 - Sage Mitchell

-2
如果你查看 Ruby sockets 的 rdoc,你会发现它们没有实现 gets。这让我相信,gets 是由更高层次的抽象(比如 IO 库)提供的,可能不知道像“连接关闭”这样的套接字特定的事情。
尝试使用 recvfrom 代替 gets

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接