Asynchronous Socket Lib

最近看了不少Internet ProtocolTCP/IP Protocol.Net Network Programming方面的資料,也動手做了一些實踐,把所得總結爲如下的文字。

1.   Overview of Socket in .Net

如果做大型的分佈式應用,且要求有很高的實時性,通常我們會使用TCP/IP協議來讓clientserver進行通信——傳遞命令和數據(比如XML Stream)。這個時候我們就需要使用異步socket了。.Net Framework提供了Socket類,此類對WinSock進行了比較好的包裝,隱藏了很多細節,大大簡化了我們需要做的工作;另外還提供了更高層次的抽象的類——TcpListener類和TcpClient類。而且,.Net Framework還提供了NetworkStream來簡化讀取和發送數據的工作。但是TcpListenerTcpClientNetworkStream都是使用blocking call的方式,而這並不是我們想要的——我們需要異步的方式——高實時性要求我們必須使用異步Socket。再者,我們還要注意到TCP協議的特點——它不會幫我們區分消息的邊界——比如我們分別發送了10K20K字節的數據出去,到了接收端的緩衝區中,這兩條消息將是30K字節的一大塊數據——這就需要我們自己建立一個機制來區分不同的消息。相對的,UDP協議將會爲我們做區分消息邊界的工作,就省去了區分不同的消息的工作。所以我們在做使用TCP協議通信的程序的時候,我們通常需要先定一個自己的通信協議。另外,還有一些實現上的細節需要注意的:多線程、server如何支持連接無限數量(當然是有限資源範圍內的無限)的client、如何確定client已經斷開了socket連接、如何區別client socket連接和server如何給特定的client發送信息,是否需要Queue來暫存消息等等問題。

2.   Consider and Discuss

其實上面已經提到了一些需要考量的問題。我把我認爲需要考慮的問題列舉如下:

1、  需要適應的網絡環境。比如局域網、廣域網和InternetInternet環境中通常需要考慮穿透防護牆問題,祕密通信問題(加密),而局域網環境中通常不需要考慮防護牆問題。

2、  Server支持連接無限量的client,且能區分開每個client和給特定的client發送消息。

3、  緩存消息。比如使用消息隊列來存放消息。如果客戶端數量非常大,那使用隊列(能把消息存放到磁盤中)來保存消息(這些消息是需要業務模塊去處理的)就有必要了,這樣即使down機了也能恢復回去。

4、  Serverclient的通信協議。這裏的協議不是指TCP這樣的協議,而是我們的應用中約定的協議,比如消息格式。

5、  如何使用線程,即如何管理線程來實現異步通信。

6、  如何確定client已經斷開了socket連接。

3.   Implement Asynchronous Socket

就我自己的瞭解,實現異步Socket大致有四種方法:

1、  使用Socket類的異步方法。

2、  使用Socket類的同步方法,但自己創建Thread來實現異步通信。

3、  使用Socket類的同步方法,但使用.Net FrameworkThreadPool類來實現異步通信。

4、  使用Socket類的同步方法,但使用自己寫的ThreadPool來實現異步通信。

下面就這幾種方法分別說明之。

3.1. AsyncCallback Method

此種方法可能是大家用的比較多的一種方法,也是一種最簡單的方法。MSDN中講如何實現異步socket的時候就講的是使用AsyncCallback Method——使用socket類提供的BeginXXXEndXXX這樣的方法。這樣方法實際上都是使用了線程池——.net framework中的ThreadPool類。此類在一個Process中只能有一個實例,任何BeginXXXEndXXX方法都使用了此類,默認最大線程數量是25個,當然也可以修改此限制,具體做法爲請看這裏

下面是一些AsyncCallback Method的例子:

MSDN上的例子

CodeGuru上的例子

CodeProject上的例子

3.2. Synchronous Method & Thread

使用.net ThreadPool類的話,自己不能對Thread進行控制。而且此ThreadPool類有一些缺點(比如不適合於長時間執行某操作的場合)。這個時候我們可以直接創建Thread來實現異步,這個時候我們自己需要去做更多的工作——使用一個Thread去接收TCP連接,對於每個連接還需要開一個Thread去接收數據,另外還需要注意線程的同步、資源的釋放和各種異常情況的處理;另外,性能也是一個必須重視的問題。

CodeProject上的例子

3.3. Synchronous Method & .Net ThreadPool

其實這麼做和使用AsyncCallback Method實質上是一樣的——都是使用了.Net ThreadPool。和Synchronous Method & Thread不同的就是不用自己去管理線程了,而把線程管理的複雜性交給ThreadPool類來做了,它爲我們使用多線程提供了便利。而且,.Net ThreadPool充分利用CPU的時間片,提供了比較高的性能,也有人做了測試證明使用ThreadPool通常會獲比直接創建和管理多個線程更爲理想的性能。

3.4. Synchronous Method & Own ThreadPool

如果不想使用.Net ThreadPool類,那我們完全可以自己寫一個ThreadPool.Net ThreadPool有一些缺點,其中主要的幾個爲:

1、  一個Process中只能有一個實例,它在各個AppDomain是共享的。ThreadPool只提供了靜態方法,不僅我們自己添加進去的WorkItem使用這個Pool,而且.net framework中那些BeginXXXEndXXX之類的方法都會使用此Pool

2、  所支持的Callback不能有返回值。WaitCallback只能帶一個object類型的參數,沒有任何返回值。

3、  不能方便地修改最小和最大線程數。ThreadPool最大線程數爲25個,有的情況下可能不夠用的。如果要修改限制還頗費周折——需要使用COM

4、  不適合用在長期執行某任務的場合。我們常常需要做一個Service來提供不間斷的服務(除非服務器down掉),但是使用ThreadPool並不合適。

我們自己寫ThreadPool的話,可以使用Hashtable來存放Thread,再實現WorkItemWorkItemQueue這樣的類來排隊Work Item,使用WaitHandle來做同步。早已經有人因不滿.net ThreadPool而編寫了自己的ThreadPool,個人認爲比較好的有SmartThreadPoolXYThreadPool

把對線程的管理交給ThreadPool後,我們就可以專注於通信方面的工作了。

4.   My Asynchronous Socket Lib

我實現的一個簡單的TCP Asynchronous Socket Lib有如下特點:

1、  適合於局域網和Internet的網絡環境。

2、  Server能接收無限量的client連接——當然受server資源的限制。

3、  Server會保持client的連接一段時間(timeout),以便於serverclient發送消息。這樣可以保證即使client端在防火牆內,server也能給client發送消息。

4、  使用Length-leading的消息格式。通常區分TCP消息邊界有三種方式:(1)發送固定長度的消息(2)使用Length-leading的消息(3)使用消息分隔符。

5、  對於server,接收到的消息存放到queue中,並觸發事件。即使server down掉,消息也不丟失。

6、  對於server,待發送的消息也存入queue中,並觸發事件。

7、  使用一個Thread Pool(不是.net framework提供的ThreadPool)來管理線程。分別有線程去做接收連接請求、檢測連接是否可用和有無消息、接收消息和檢查超時的工作。

8、  提供很多事件,比如OnServerStartOnNewClientOnReceiveMessageOnServerErrorOnServerStop等等。

9、  檢查client連接。Client自己斷開了連接後,在server端從socketAvailable屬性並不能判斷出來,可行的辦法是使用Poll方法,然後看是否能peek到數據;或者接收數據的時候捕捉異常。就我自己實踐的結果,使用Poll方法會有一些麻煩:我是將client連接進來的socket存放到Hashtable中的,但是調用了Poll方法後,會使此Hashtable被改變,從而導致迭代此Hashtable失敗。CodeGuru上的例子介紹了使用異常的方法。

 

 

希望此文能對大家有一點幫助,也希望有經驗的朋友多多批評指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章