我正在 Ruby 3.0.2 中構建一個 TCPServer,我發現我似乎無法在不阻塞的情況下讀取整個資料包(直到套接字被關閉)。
編輯:我對自己想要做的事情有些困惑--我的錯--所以只是為了幫助澄清:我想要讀取到目前為止通過 TCP 連接發送的所有資料。(結束編輯)
我的第一次嘗試是:
#!/snap/bin/ruby。
require socket'
server = TCPServer.new('localhost', 4200)
回圈 {
Thread.start(server.accept) do |connection|
puts connection.get # The important line
end。
}
但是這就掛了,直到客戶端關閉連接。好吧,我看了一下connection.methods和ruby docs,并嘗試了一堆看起來很有希望的選項。基本上,有兩種型別的讀取方法:阻塞式和非阻塞式。
我嘗試的阻塞方法是.read, .gets, .readlines, .readline, .recv, 和.recvmsg。現在.read、.readlines和.gets都掛起(直到套接字被關閉)--所以這沒有什么用。其他的(例如,.readline,recv方法)并不讀取整個訊息。現在,我可以讀取每一行,直到我看到一個空行,然后從那里決議HTTP頭。但肯定有更好的方法;我不想因為沒有讀取頭檔案末尾的空行而擔心得到一個損壞的訊息并掛掉。
所以我去看了一下非阻塞選項。特別是.recv_nonblock和.recvmsg_nonblock。這兩個選項都出現了錯誤(Resource temporarily unavailable - recvfrom(2) would block和Resource temporarily unavailable - recvmsg(2) )。
對可能發生的情況有什么想法嗎?我認為這與我使用 Ruby 3 有關,因為在 Ruby 2.5 上嘗試代碼時,client.get 回傳了一行(沒有掛起),盡管.readlines 確實掛起 - 所以不確定發生了什么。
事實上,我可以直接呼叫client.get_message,我將獲得已經發送的整個訊息,但我也可以在TCP級別作業,獲得資料包的大小,讀取該大小,并從那里重建訊息。
uj5u.com熱心網友回復:
TCP只是傳輸你寫入套接字的位元組,并保證按照發送的順序接收。如果你有 "訊息 "的概念,那么你就需要把它添加到你的服務器和客戶端。
.gets具體來說,它將阻塞,直到讀到一個新的 "行",或者任何你定義為字串的分隔符 - 參見檔案IO#gets。這意味著在您的服務器收到來自客戶端的該位元組之前,它將被阻止。
在你的客戶端中,看看你是如何寫入資料的 - 如果你使用 ruby,那么 puts 將會起作用,因為它將用一個新行來終止字串。如果你使用的是write,那么它將只寫字串而不寫新行
例如:
# client.rb
c = TCPSocket.new 'localhost', 5000。
c.put "foo".
c.寫"bar"。
c.寫 "baz
"/span>
# server.rb
s = TCPServer.new 5000
回圈 do
client = s.accept
把client.get
把client.get
end
將輸出
foo
巴巴茲
uj5u.com熱心網友回復:
感謝每個人的評論/回答,但我找到了我認為是Socket類創建者所希望的解決方案!
<recv_nonblock方法需要一些可選的引數--其中之一是一個緩沖區,Socket將存盤它所讀取的內容。所以像client.recv_nonblock(1000, 0, buffer)這樣的呼叫會將Socket中的1000個字符存盤到buffer中,然后退出而不是阻塞。
為了使生活變得簡單,我給TCPSocket類打了一個猴子補丁:
class TCPSocket<
def eat_buffer
contents = ''/span>
buffer = ''/span>
begin
回圈 {
recv_nonblock(256, 0, buffer)
contents = buffer
}
rescue IO::EAGAINWaitReadable
內容
結束
end
end end
Steffen在評論中提出的觀點很有道理--TCP并不是被設計成這樣使用的。這是一個黑客(在壞的意義上)的方法,應該被避免。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/307597.html
標籤:
