2017-11-11 121 views
1

塊I具有用於Ruby的一個簡單的TCP服務器下面的代碼:Socket.read()將不會在紅寶石

# 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.readserver.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

read將會阻塞,如果沒有數據,但不在EOF。所述IO#read docs say

當這個方法被稱爲在文件末尾,則返回nil"",取決於長度readread(nil),和read(0)返回""read(positive_integer)nil返回。

由於在EOF上調用read不會阻塞,因此select將立即返回IO。

在您的代碼中,第一次調用read將阻塞,直到全部數據從連接中讀取(即另一端已關閉它)。從那時起它將在EOF,所以select將返回它準備就緒,read將立即返回一個空字符串。

+0

你知道嗎,如果有什麼辦法阻止EOF?我在玩一個簡單的聊天服務器和客戶端,我希望在客戶端和服務器之間有一個長期的連接。我想要的最後一件事是服務器去100%cpu只是主動循環套接字大部分時間沒有數據。 – Reck

+0

您可能想要檢測並刪除這些連接,因此您不想跳過這些連接。請注意,沒有可用數據的套接字(它仍然連接到另一端)和EOF中的套接字(已斷開連接並且您只需關閉它)之間存在差異。 – matt

+0

你絕對正確! – Reck