[英]Weird behavior of async tcp socket
我有一個異步服務器套接字偵聽某個端口。 然后從另一台電腦,我連接到服務器並發送1個字節。 一切正常,但有一種奇怪的行為。 當我拉網線並嘗試發送1個字節(在os實現拉線之前),我沒有得到任何異常/錯誤,並且正如預期的那樣,服務器不接收該數據包。 這是套接字應該如何工作? 這是否意味着在連接丟失的情況下,某些數據包可能會丟失(因為我沒有得到異常並且不知道該請求未被發送)? 這是代碼:
private void button3_Click(object sender, EventArgs e)
{
var b = new byte[1] {1};
client.BeginSend(b, 0, b.Length, 0, new AsyncCallback(SendCallback), client);
}
private void SendCallback(IAsyncResult ar)
{
Socket client = (Socket)ar.AsyncState;
int bytesSent = client.EndSend(ar);
this.Invoke(new MethodInvoker(() => { MessageBox.Show(bytesSent.ToString() + " bytes sent"); }));
}
發送者怎么可能告訴沒有收到數據包? 它將它發送到黑洞並等待回復。 只要沒有回復,他就無法知道數據包是收到還是永遠不會收到。
這通常通過超時來解決。 最終,TCP堆棧將聲明連接已停止,或者您的后續讀取超時。
發送不保證交付。
請對方發送確認信。 您的確認閱讀將最終超時。
或者, Shutdown(Send)
套接字。 這確保了交付並將拋出異常(超時后)。 在關閉套接字之前,您應該Shutdown(Both)
套接字Shutdown(Both)
,以確保您收到所有錯誤的通知。
套接字層的Windows在內核維護套接字緩沖區。 在應用層成功發送只是意味着數據被復制到內核緩沖區中。 此緩沖區中的數據由TCP堆棧推送到遠程應用程序。 在Windows中,如果丟棄數據包,TCP會重新發送數據3次,之后TCP堆棧會通知靠近應用程序的連接。 重試之間的間隔由RTT決定。 第一次重試是在1 * RTT之后,第二次是在2 * RTT之后,第三次是在3 * RTT之后。 在您的情況下,您發送的1個字節只是復制到內核緩沖區並顯示成功。 它將需要3 * RTT來指示套接字已關閉。 僅當您調用任何套接字API或監視套接字以獲取close事件時,才會通知此連接關閉。 拔出電纜后,如果在3 * RTT之后准確排隊第二次發送,則應通過異常發送。 另一種立即獲得發送失敗指示的方法是將發送套接字緩沖區大小設置為零(SetSocketOption(..,SendBuffer,..)),以便TCP堆棧直接使用緩沖區並立即指示失敗。
我假設你創建了一個TCP套接字。
在這種情況下,如果斷開連接,您的客戶將不會收到通知(除非您的應用程序發送了某種注銷消息)。
查看TcpClient的Connected
屬性:
Connected屬性從最后一個I / O操作開始獲取Client套接字的連接狀態。 當它返回false時,Client套接字從未連接,或者不再連接。
“截至上一次I / O”操作:只有來自客戶端的(不成功)讀取將幫助您檢測斷開連接。 許多應用程序實現“ping”以檢測某些斷開連接。
是的,這是TCP套接字的工作方式。 不,這並不意味着數據包必然丟失。
發生在幕后的是這個。 當您調用BeginSend時,您發送的字節將傳遞到操作系統的TCP堆棧。 然后,TCP堆棧以數據包的形式發送數據。
當數據包未在合理的時間內得到確認時,TCP堆棧會自動重新發送數據包。 只要沒有收到確認包,就會重復發生這種情況。
如果您重新插入電纜,其中一個重新發送將通過,服務器將很快看到數據。 這是期望的行為。 請記住,TCP旨在傳輸冷戰期間的軍事數據; 這個想法是,即使網絡的一部分被破壞,路由系統最終也會調整以找到接收者的另一條路徑,並且數據最終會通過。
如果不重新插入電纜,大多數TCP堆棧最終會放棄,終止連接。 然而,這需要幾分鍾。
有關TCP重傳超時的更多信息,請參見RFC 1122:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.