2010-03-27 30 views
3

我有連接到TCP服務器(即netcat)的這個ruby代碼。它循環20次,併發送「ABCD」。如果我殺死netcat,則需要兩次迭代才能觸發異常。在netcat被終止後的第一個循環中,不會觸發異常,「send」報告已經正確寫入了5個字節......哪一個最終不是真的,因爲服務器當然沒有收到它們。當服務器被終止時,Ruby TCPSocket不會注意到它

有沒有辦法解決這個問題?現在我正在丟失數據:因爲我認爲它已被正確傳輸,我沒有重播它。

#!/usr/bin/env ruby 
require 'rubygems' 
require 'socket' 

sock = TCPSocket.new('192.168.0.10', 5443) 
sock.sync = true 

20.times do 
    sleep 2 
    begin 
    count = sock.write("ABCD ") 
    puts "Wrote #{count} bytes" 
    rescue Exception => myException 
    puts "Exception rescued : #{myException}" 
    end 
end 

回答

1

我想沒有sleep功能(只是爲了確保它不把在嫌棄)和仍然沒有運氣:

#!/usr/bin/env ruby 
require 'rubygems' 
require 'socket' 
require 'activesupport' # Fixnum.seconds 

sock = TCPSocket.new('127.0.0.1', 5443) 
sock.sync = true 

will_restart_at = Time.now + 2.seconds 
should_continue = true 

while should_continue 
    if will_restart_at <= Time.now 
    will_restart_at = Time.now + 2.seconds 
    begin 
     count = sock.write("ABCD ") 
     puts "Wrote #{count} bytes" 
    rescue Exception => myException 
     puts "Exception rescued : #{myException}" 
     should_continue = false 
    end 
    end 
end 

我分析了使用Wireshark和兩種溶液恰好表現相同。

我認爲(並不能確定),直到你真的打電話your_socket.write(這將不會因爲套接字仍然打開,因爲你沒有探測其可能的破壞),套接字不會提高錯誤。

我試圖用nginx和手動TCP套接字來模擬這個。並看看:

irb> sock = TCPSocket.new('127.0.0.1', 80) 
=> #<TCPSocket:0xb743b824> 
irb> sock.write("salut") 
=> 5 
irb> sock.read 
=> "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n</body>\r\n</html>\r\n" 

# Here, I kill nginx 

irb> sock.write("salut") 
=> 5 
irb> sock.read 
=> "" 
irb> sock.write("salut") 
Errno::EPIPE: Broken pipe 

那麼從這裏得出的結論是什麼?除非你真的希望從服務器獲得一些數據,否則你會被檢測出你已經失去了連接:)

+0

我已經注意到,在這個套接字上讀取的IO.select確實返回true。所以至少如果你使用它,你會知道閱讀。它會拋出一個錯誤。這比靜靜地失敗要好得多。感謝您發佈這個答案。這讓我感覺更好,其他人看到這種奇怪的行爲。注意:我在jruby和mri 1.9.3中看到了這個。 – kbrock 2013-05-01 20:37:46

1

要檢測一個正常關閉,你必須從套接字讀取 - 讀取返回0表示套接字已關閉。

如果您確實需要知道數據是否已成功發送,除了在應用程序級別實現數據的確認外,沒有辦法。

+0

確實。所以這幾乎意味着我被搞砸了? 「二進制接口使用對二進制內容純TCP套接字;在自然界流和具有零級的確認響應。」 http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS /CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3 – Ecco 2010-03-28 01:00:28

2

發送數據時,當數據寫入TCP輸出緩衝區時,阻塞調用將返回。它只會在緩衝區已滿的情況下阻塞,等待服務器確認收到之前發送的數據。

一旦此數據在緩衝區中,網絡驅動程序嘗試發送數據。如果連接丟失,則在第二次嘗試寫入時,應用程序會發現連接中斷狀態。

另外,連接如何關閉?服務器是否正在關閉連接?在這種情況下,客戶端套接字將在其下一個套接字調用處通知。或者它墜毀了?或者可能是網絡故障,這意味着你不能再進行通信。

僅當您嘗試通過套接字發送或接收數據時,纔會發現斷開的連接。這與連接主動關閉不同。您根本無法確定連接是否仍然存在,而無需執行任何操作。

因此,在寫入之後嘗試執行sock.recv(0) - 如果套接字失敗,將會引發「Errno::ECONNRESET: Connection reset by peer - recvfrom(2)」。您也可以嘗試sock.sendmsg "", 0(不是sock.write或sock.send),這會報告「Errno::EPIPE: Broken pipe - sendmsg(2)」。

即使您掌握了TCP數據包並獲得了數據已在另一端收到的確認,仍然無法保證服務器將處理此數據 - 它可能在其輸入緩衝區中但尚未處理。

所有這些都可能有助於早期識別斷開的連接,但它仍不能保證服務器收到數據和處理。知道應用程序處理了您的消息的唯一可靠方法是使用應用程序級響應。

+0

爲了將來的參考 - 這個方法的名稱中有一個輸入錯誤(它是'sendmsg',而不是'send_msg')。對於那些肯定會證明是致命的公然拷貝。如果編輯時沒有6個字符的限制,我會自行更正。 – Tomalla 2016-07-25 18:55:54

相關問題