Socket.read()不会在Ruby中阻塞

时间:2017-11-11 18:33:22

标签: ruby sockets

对于Ruby中的简单TCP服务器,我有以下代码:

# server.rb
require 'socket'

class Server
  def initialize(port)
    @port = port
  end

  def run
    Socket.tcp_server_loop(@port) do |connection|
      Thread.new do
        loop do
          puts "IO: #{IO.select([connection]).inspect} - data: #{connection.read}"
        end
      end
    end
  end
end

server = Server.new(16451)
server.run

以及这个简单的TCP客户端代码:

# client.rb
require 'socket'

client = TCPSocket.new('localhost', 16451)
client.write('stuff')

据我了解,如果套接字上没有数据,connection.read中的server.rb应该阻止。但是,当我在我的macbook(OS X 10.12.5)上运行它时,它会继续吐出以下输出:

IO: [[#<Socket:fd 12>], [], []] - data: stuff
IO: [[#<Socket:fd 12>], [], []] - data: 
IO: [[#<Socket:fd 12>], [], []] - data: 
IO: [[#<Socket:fd 12>], [], []] - data: 
IO: [[#<Socket:fd 12>], [], []] - data: 
IO: [[#<Socket:fd 12>], [], []] - data: 
IO: [[#<Socket:fd 12>], [], []] - data: 
IO: [[#<Socket:fd 12>], [], []] - data: 
...

似乎IO.select认为在套接字上有可读取的数据,而没有发送此类数据。

在Ruby中使用套接字时如何实现阻塞读取?我忽略了什么吗?

马特的回答指出了我正确的方向。对于未来的读者,这是我的新代码。

# server.rb
require 'socket'

class Server
  BYTESIZE_OF_PACKED_INTEGER = [1].pack('i').bytesize

  def initialize(port)
    @port = port
  end

  def run
    Socket.tcp_server_loop(@port) do |connection|
      Thread.new do
        while packed_msg_bytesize = connection.read(BYTESIZE_OF_PACKED_INTEGER)
          msg_bytesize = packed_msg_bytesize.unpack('i').first
          msg = connection.read(msg_bytesize)
          puts msg
        end
      end
    end
  end
end

server = Server.new(16451)
server.run

客户端代码。

# client.rb
require 'socket'

msg = 'stuff'
msg_bytesize = msg.bytesize
packed_msg_bytesize = [msg_bytesize].pack('i')

client = TCPSocket.new('localhost', 16451)
client.write(packed_msg_bytesize)
client.write(msg)

1 个答案:

答案 0 :(得分:1)

如果没有数据,

read将阻止,但在EOF时不会阻止。 IO#read docs say

  

在文件末尾调用此方法时,它会返回nil"",具体取决于长度readread(nil),并read(0)返回""read(positive_integer)返回nil

由于在EOF上调用read没有阻止,select会立即将IO返回。

在您的代码中,对read的第一次调用将阻塞,直到 all 从连接中读取数据(即另一端已关闭它)。从那时起它将在EOF,因此select会将其恢复为准备状态,read将立即返回一个空字符串。