聊天室範例 - 使用c#撰寫非同步方法tcp socket




.NET沒有可靠的異步套接字通信? (4)

Hmya,這不是一個.NET框架問題。 鏈接的知識庫文章可能會更加明確一些:“你正在使用一把加載槍,這是當你瞄准你的腳時發生的事情”。 槍中的子彈是.NET,讓你能夠啟動盡可能多的異步I / O請求。 它會做你要求它做的,直到你遇到某種資源限制。 在這種情況下,可能在第0代堆中有太多的固定接收緩衝區。

資源管理仍然是我們的工作,而不是.NET的。 分配無限的內存沒有什麼不同。 解決這個問題需要你對未完成的BeginGetResponse()請求的數量進行限制。 有數以百計的沒有意義,他們每個人都必須一次一個地擠過Intertube。 添加另一個請求只會導致完成所需的時間更長。 或者崩潰你的程序。

我曾經在.NET中寫過一個Crawler。 為了提高它的可伸縮性,我試圖利用.NET的異步API。

System.Net.HttpWebRequest具有異步API BeginGetResponse / EndGetResponse。 但是,這對API只是獲取HTTP響應頭和一個Stream實例,我們可以從中提取HTTP響應內容。 所以,我的策略是使用BeginGetResponse / EndGetResponse異步獲取響應Stream,然後使用BeginRead / EndRead從響應Stream實例中異步獲取字節。

在Crawler進行壓力測試之前,一切似乎都是完美的。 在壓力測試下,Crawler遭受高內存使用。 我用WinDbg + SoS檢查了內存,並發現大量的字節數組是由System.Threading.OverlappedData實例綁定的。 在互聯網搜索後,我發現這個KB http://support.microsoft.com/kb/947862從微軟。

根據知識庫,異步I / O的數量應該有一個“上限”,但它不會告訴“建議”的界限值。 所以,在我看來,這個KB沒有任何幫助。 這顯然是一個.NET的錯誤。 最後,我不得不放棄從響應流中異步提取字節的想法,只是以同步的方式進行。

允許具有點網絡套接字的異步IO的.NET庫(Socket.BeginSend / Socket.BeginReceive / NetworkStream.BeginRead / NetworkStream.BeginWrite)必須具有未完成的緩衝區數量(發送或接收)的上限與他們的異步IO 。

網絡應用程序應該對發布的未完成異步IO的數量有一個上限。

編輯:添加一些問號。

任何人有任何經驗在Socket和NetworkStream做異步I / O? 一般來說,生產中的爬蟲是否使用同步或異步的互聯網進行I / O?


您顯然希望限制並發請求的數量,而不管您的抓取工具是否同步/異步。 這個限制是不固定的,這取決於你的硬件,網絡,... ... -

我不太確定這裡有什麼問題,因為HTTP /套接字的.NET實現是“好的”。 有一些漏洞(請參閱我的帖子,關於正確控制超時),但它完成了工作(我們有一個生產履帶每秒獲取數百頁)。

順便說一句,我們使用同步IO,只是為了方便。 每個任務都有一個線程,我們限制並發線程的數量。 對於線程管理,我們使用Microsoft CCR


當您使用套接字的異步發送(BeginSend)方法時發生這種情況。 如果您使用自己的自定義線程池,並通過線程發送數據同步發送方法主要是解決這個問題。 經過測試和證明。


這不僅限於.Net。

這是一個簡單的事實,每個異步請求(文件,網絡等)使用內存和(至少對於網絡請求)非頁面緩衝池(請參閱這裡了解可以在非託管代碼中獲得的問題的詳細信息)。 未完成請求的數量因此受內存量的限制。 在Vista之前,有一些嚴重的非頁面緩衝池限制,在內存耗盡之前會導致問題,但是在Vista之後的環境下,非頁面緩衝池的使用情況要好得多(請看這裡 )。

這在託管代碼中稍微複雜一些,除了在非託管環境中遇到的問題之外,還必須處理這樣一個事實:用於異步請求的內存緩衝區將被鎖定,直到這些請求完成。 聽起來就像你在讀取時遇到了這些問題,但是對於寫操作來說,如果不是更糟的話,一旦TCP流量控制在連接上啟動,那些發送完成將開始花費更長的時間,因此這些緩衝區固定時間越來越長 - 見這里這裡 )。

問題不在於.net異步的東西被破壞,只是抽像是這樣,它使得它看起來比實際上更容易。 例如,為了避免鎖定問題,請在程序啟動時將所有緩衝區分配到一個大的連續塊中,而不是根據需要進行分配。

就我個人而言,我會在非託管代碼中編寫這樣一個爬蟲程序,但這只是我;)您仍然會遇到很多問題,但是您對它們有更多的控制權。





web-crawler