2014-11-01 72 views
5

我有一個關於this問題(「異步服務器插座多個客戶端」)的問題。微軟異步服務器插座示例

無論是微軟改變the example因爲格魯斯答案,或者我真的不明白這一點 - 在本例中 它說:

 while (true) { 
      // Set the event to nonsignaled state. 
      allDone.Reset(); 

      // Start an asynchronous socket to listen for connections. 
      Console.WriteLine("Waiting for a connection..."); 
      listener.BeginAccept( 
       new AsyncCallback(AcceptCallback), 
       listener); 

      // Wait until a connection is made before continuing. 
      allDone.WaitOne(); 
     } 

據我瞭解BeginAccept()函數在同時contiuously稱爲(真)循環,只有被調用AcceptCallback()函數後纔會停止,因爲發生的第一件事是allDone.Set()。

但是Groo所述

與MSDN例子的問題是,它允許僅單個客戶端的連接(listener.BeginAccept只調用一次)。

而實際上我不知道爲什麼ManualResetEvent的allDone在所有使用... 我認爲listener.EndAccept(AR)方法阻止反正。

Lisner.BeginAccept()如果在仍然運行時第二次調用時拋出異常? 但如果是這樣,爲什麼allDone.Set()在listener.EndAccept(ar)之前?

而另一個問題:我收到了EOF,後

可我只是調用handler.BeginReceive(...),在ReadCallback(IAsyncResult的AR)函數來等待來自同一更多進來的數據客戶?

任何有經驗的人都可以向我解釋一下嗎?

謝謝!

+1

我已經回答了一段時間,但是IIRC最初的例子沒有循環。我們可以查看[那段時間的互聯網檔案頁面](http://web.archive.org/web/20110201000000*/http://msdn.microsoft.com/en-us/library/fx6588te.aspx),但目前服務器似乎正在關閉。 – Groo 2014-11-03 13:27:21

+0

然後有一箇舊的問題,因爲它有一個鏈接到更新後的源代碼... – fosb 2014-11-14 18:27:09

回答

7

有可能這個例子實際上已經更新了,因爲其他的答案已經發布了。或者,回答者Groo可能根本沒有完全理解這個例子。在任何一種情況下,你都是正確的觀察他的聲明,即只有一個客戶可以被接受是不正確的。

我同意usr寫的一些內容,但對整個事情有一些不同的看法。另外,我認爲這些問題應該得到更全面和具體的處理。首先,雖然我同意優秀的設計通常是在accept回調方法中發出對BeginAccept()的後續調用,而不是使用循環,但本例中的實現沒有任何問題。在上一次呼叫完成後,不會再有新的電話號碼BeginAccept()發出;事件句柄用於同步調用BeginAccept()的線程和任何處理完成的線程。第一個線程僅在先前發出的接受完成時才被釋放,然後在線程再次阻塞之前,只有一個新的BeginAccept()調用被創建。

這有點尷尬,但完全是猶太教。這個樣本的作者可能認爲,既然在他的控制檯程序中,他將有一條線程在那裏閒置,他不妨給它做點什麼。 :)

無論如何,要回答問題#1:你是正確的,目前在該鏈接上的示例確實允許多個客戶端連接。

問題#2,爲什麼使用事件句柄,希望上面的解釋已經回答了。這個例子用來釋放正在調用BeginAccept()的線程,以便在前一個調用完成後再次調用它。

問題#3,EndAccept()是否阻塞?有點。如果您在接受操作實際完成之前致電EndAccept(),那麼是的。它會阻止。但在這種情況下,只有在完成回調被調用時纔會調用它。在這一點上,我們可以肯定的是,EndAccept()的呼叫將而不是塊。它所要做的就是檢索完成操作的結果(當然,假設沒有例外)。

問題#4,在調用EndAccept()之前第二次調用BeginAccept()是否合法?是的,即使有多個接受操作排隊(它是)是不合法的。在此,對BeginAccept()的呼叫發生在第一個BeginAccept()的完成回叫中。也就是說,雖然代碼尚未調用EndAccept(),但接受操作本身已完成,所以它甚至不是多個接受操作未完成的情況。接收和發送操作同樣自由;在發生任何事情之前,您可以合法地多次調用所有這些方法。

問題5,即使我收到<EOF>,我可以撥打BeginReceive()嗎?是。實際上,這是一個MSDN示例有缺陷的區域,因爲它確實是而不是一旦接收到最後一個預期數據就會繼續接收。實際上,直到接收完成0字節時,它應該仍然總是再次呼叫BeginReceive(),不管是否需要更多數據,然後通過在那一點調用Shutdown(SocketShutdown.Both)來處理字節計數爲0的完成的接收,以發信號通知確認優美關閉連接(假設它已經完成了發送,那時它已經調用Shutdown(SocketShutdown.Send) ...如果沒有,它應該只使用SocketShutdown.Receive和/或根本不調用關機,直到它完成發送並且它可以使用SocketShutdown.Both ... SocketShutdown.Receive實際上並沒有對連接本身做任何重要的事情,所以等到SocketShutdown.Both是合適的是合理的)。

換句話說,即使服務器肯定客戶端不會發送任何額外的數據知道,正確使用套接字API的是仍然嘗試另一個接收操作,查找0字節的回報值,表示客戶端實際上已經開始關閉連接。只有在此時服務器才能開始自己的關閉進程並關閉套接字。

最後,你沒有問,但是因爲usr提出了它:我不同意這個MSDN示例今天沒有關聯。不幸的是,微軟沒有爲Socket類提供基於任務的異步API版本。有支持異步/等待的其他網絡API(例如TcpClient/NetworkStream),但如果您想直接使用Socket類,則會陷入舊的異步模式(Socket有兩個,都是基於回調的)。

您可以將Tasks中的同步Socket方法作爲舊API的替代方法進行打包,但是您將失去Socket類中基於I/O完成端口的異步API的優勢。更好的辦法是某種兼容任務的包裝,它仍然使用下面的異步API,但實現起來有些複雜,我現在還沒有意識到這種事情(但它肯定可以存在,所以可能會如果你喜歡使用async/await,那麼值得在你的網站上進行一些網絡搜索)。

希望有幫助!我會爲長答案道歉,但這是一個相當廣泛的問題(幾乎太寬泛,但對我來說,它似乎仍然在合理的界限:))。

+0

哇,謝謝你在這個細節上回答@Peter Duniho! 我真的很感激!感謝您的幫助和時間人。 – fosb 2014-11-02 14:02:39

0

樣品很混亂。該事件不是必需的。相反,接受回調應該發出下一個接受操作,以便總是有一個接受未完成的。

在未節流循環中調用BeginAccept將是不正確的,因爲這會啓動無限數量的未完成接受操作。

您是否知道自引入await以來,舊的APM已過時?所有這些代碼今天都沒有關係。

此外,請注意,Web上的大多數套接字代碼都有不同的方式存在嚴重缺陷。

+0

感謝您的答案,它會帶領我到以下問題: >您是否知道自引入等待舊的APM已經過時了嗎?所有這些代碼今天都沒有關係。 – fosb 2014-11-01 22:25:33

+0

對不起,偶然創建了前一個答案... @usr但感謝您的回答,它導致我到以下問題: *下一個接受回調是調用listener.BeginAccept()?我有沒有那麼正確,然後listener.EndAccept()正在等待下一個輸入連接? *你認爲用TCPListener和TCPClient去更好嗎? 「您是否知道自從引入等待舊APM過時?所有這些代碼今天都沒有相關性。」 你的意思是用EAP更好嗎? 再次感謝^^ – fosb 2014-11-01 22:33:45

+0

開始和結束已配對。任何給定的結束呼叫匹配一個開始呼叫並獲取其結果。 End只是給你已經接受的連接。接下來,再次調用Begin開始接受。 TcpListener/Client是很薄的包裝器。他們是優先考慮的。 EAP也是過時的。使用NetworkStream等待。使用異步IO方法,如ReadAsync。 – usr 2014-11-01 22:35:45