簡體   English   中英

NetworkStream.Write 立即返回 - 我如何知道它何時完成發送數據?

[英]NetworkStream.Write returns immediately - how can I tell when it has finished sending data?

盡管有文檔,但 NetworkStream.Write 似乎並沒有等到數據發送完畢。 相反,它會等到數據被復制到緩沖區然后返回。 該緩沖區在后台傳輸。

這是我目前擁有的代碼。 無論我使用 ns.Write 還是 ns.BeginWrite 都沒有關系 - 兩者都會立即返回。 EndWrite 也立即返回(這是有道理的,因為它正在寫入發送緩沖區,而不是寫入網絡)。

    bool done;
    void SendData(TcpClient tcp, byte[] data)
    {
        NetworkStream ns = tcp.GetStream();
        done = false;
        ns.BeginWrite(bytWriteBuffer, 0, data.Length, myWriteCallBack, ns);
        while (done == false) Thread.Sleep(10);
    }
   
    public void myWriteCallBack(IAsyncResult ar)
    {
        NetworkStream ns = (NetworkStream)ar.AsyncState;
        ns.EndWrite(ar);
        done = true;
    }

如何判斷數據何時實際發送到客戶端?

我想在發送我的數據后等待 10 秒(例如)等待來自服務器的響應,否則我會假設有問題。 如果發送我的數據需要 15 秒,那么它將始終超時,因為我只能從 NetworkStream.Write 返回時開始計數 - 這是在發送數據之前。 我想從數據離開我的網卡開始計算 10 秒。

數據量和發送時間可能會有所不同 - 發送可能需要 1 秒,發送可能需要 10 秒,發送可能需要一分鍾。 服務器在收到數據后確實會發送響應(它是 smtp 服務器),但是如果我的數據格式錯誤並且響應永遠不會到來,我不想永遠等待,這就是為什么我需要知道我是否' m 等待發送數據,或者等待服務器響應。

我可能想向用戶顯示狀態 - 我想顯示“正在向服務器發送數據”和“等待來自服務器的響應” - 我該怎么做?

我不是 C# 程序員,但你問這個問題的方式有點誤導。 對於“接收”的任何有用定義,了解何時“接收”您的數據的唯一方法是在您的協議中包含一個特定的確認消息,表明數據已被完全處理。

確切地說,數據不會“離開”您的網卡。 考慮您的程序與網絡的關系的最佳方式是:

你的程序 -> 很多令人困惑的東西 -> 對等程序

可能在“很多令人困惑的東西”中的東西列表:

  • CLR
  • 操作系統 kernel
  • 虛擬化網絡接口
  • 一個開關
  • 軟件防火牆
  • 硬件防火牆
  • 執行網絡地址轉換的路由器
  • 對等端的路由器執行網絡地址轉換

因此,如果您在托管在不同操作系統下的虛擬機上,該虛擬機具有控制虛擬機網絡行為的軟件防火牆 - 數據何時“真正”離開您的網卡? 即使在最好的情況下,這些組件中的許多也可能會丟棄一個數據包,您的網卡將需要重新傳輸該數據包。 第一次(不成功)嘗試時,它是否“離開”了您的網卡? 大多數網絡 API 會說不,直到另一端發送 TCP 確認后才“發送”。

也就是說, NetworkStream.Write 的文檔似乎表明它至少在啟動“發送”操作之前不會返回:

在發送請求的字節數或拋出 SocketException 之前,Write 方法會一直阻塞。

當然,由於我上面給出的原因,“被發送”有點模糊。 也有可能數據將由您的程序“真正”發送並由對等程序接收,但對等方將崩潰或以其他方式不實際處理數據。 因此,您應該先Write ,然后Read一條消息,該消息僅在您的對等方實際處理了該消息時才會發出。

TCP 是一個“可靠”的協議,這意味着如果沒有套接字錯誤,數據將在另一端接收。 我已經看到無數次嘗試用更高級別的應用程序確認來猜測 TCP,但恕我直言,這通常是浪費時間和帶寬。

通常,您描述的問題是通過正常的客戶端/服務器設計來處理的,其最簡單的形式是這樣的......

客戶端向服務器發送請求,並在套接字上進行阻塞讀取以等待某種響應。 如果 TCP 連接出現問題,則讀取將中止。 客戶端還應該使用超時來檢測服務器的任何非網絡相關問題。 如果請求失敗或超時,則客戶端可以重試、報告錯誤等。

一旦服務器處理了請求並發送了響應,它通常不再關心發生了什么——即使套接字在事務期間消失了——因為它取決於客戶端來啟動任何進一步的交互。 就個人而言,我覺得成為服務員很舒服。 :-)

一般來說,我建議無論如何發送客戶的確認。 這樣您就可以 100% 確定數據已被接收並正確接收。

如果我不得不猜測,一旦將緩沖區交給 Windows 套接字,NetworkStream 就會認為數據已經發送。 所以,我不確定有沒有辦法通過 TcpClient 完成你想要的。

我想不出 NetworkStream.Write 不會盡快將數據發送到服務器的情況。 除非出現大規模的網絡擁塞或斷開連接,否則它應該會在合理的時間內到達另一端。 您是否有可能遇到協議問題? 例如,對於 HTTP,請求標頭必須以空行結尾,並且服務器不會發送任何響應,直到發生響應 - 使用的協議是否具有類似的消息結束特性?

這里有一些比原始版本更簡潔的代碼,刪除了委托、字段和 Thread.Sleep。 它在功能上執行完全相同的方式。

void SendData(TcpClient tcp, byte[] data) {
    NetworkStream ns = tcp.GetStream();
    // BUG?: should bytWriteBuffer == data?
    IAsyncResult r = ns.BeginWrite(bytWriteBuffer, 0, data.Length, null, null);
    r.AsyncWaitHandle.WaitOne();
    ns.EndWrite(r);
}

看起來問題在我寫上面的時候被修改了。 The.WaitOne() 可能會幫助您解決超時問題。 它可以傳遞一個超時參數。 這是一個懶惰的等待——在結果完成或超時到期之前,不會再次調度線程。

我試圖理解 .NET NetworkStream 設計師的意圖,他們必須這樣設計。 寫入后,要發送的數據不再由 .NET 處理。 因此,Write 立即返回是合理的(並且數據很快就會從 NIC 發送出去)。

所以在你的應用程序設計中,你應該遵循這個模式,而不是試圖讓它按照你的方式工作。 例如,在從 NetworkStream 接收任何數據之前使用較長的超時時間可以補償您的命令離開 NIC 之前所消耗的時間。

總之,在源文件中硬編碼超時值是不好的做法。 如果超時值可以在運行時配置,那么一切都應該正常工作。

如何使用 Flush() 方法。

ns.Flush()

這應該確保在繼續之前寫入數據。

波紋管 .net 是 windows sockets 使用 ZB136EF5F6A01D816991FE3CF7A6AC76 TCP 使用 ACK 包通知發送方數據已經傳輸成功。 因此,發送方機器知道何時傳輸了數據,但無法(據我所知)在 .net 中獲取該信息。

編輯:只是一個想法,從未嘗試過:Write() 僅在 sockets 緩沖區已滿時阻塞。 因此,如果我們將緩沖區大小(SendBufferSize)降低到一個非常低的值(8?1?0?)我們可能會得到我們想要的:)

也許嘗試設置tcp.NoDelay = true

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM