1. 程式人生 > >可擴充套件多執行緒非同步Socket伺服器框架EMTASS 2.0

可擴充套件多執行緒非同步Socket伺服器框架EMTASS 2.0

0 前言

>>[前言][第1節][第2節][第3節][第4節][第5節][第6節]
在程式設計與實際應用中,Socket資料包接收伺服器夠得上一個經典問題了:需要計算機與網路程式設計知識(主要是Socket),與業務處理邏輯密切(如:包組成規則),同時還要兼顧系統執行的穩定、效率、安全與管理等。具體應用時,在滿足業務處理邏輯要求的基礎上,存在側重點:有些需要考慮併發與效率,有些需要強調穩定與可靠等等。雖然.NET 2.0 Framework上的IOCP(I/O完成埠)非同步技術可以有效解決併發等問題,但完全的非同步模式也缺乏一些控制上的靈活性,例如:Socket暫停操作等。 本文介紹的是一個傳統Socket資料包伺服器解決方案,該方案改自筆者2005年底的一個交通部省級公路交通流量資料伺服器中心(DSC)專案。當時.NET Framework 2.0 與 Visual Studio 2005 釋出沒多久,筆者接觸C#的時間不長。於是Google了國內國外網,希望找點應用C#解決Socket通訊問題的思路和程式碼。最後,找到了兩篇幫助最大的文章:一篇是國人2005年3月寫的Socket接收器框架——
在C#中使用非同步Socket程式設計實現TCP網路服務的C/S的通訊構架(一)
(分(一)、(二)兩篇),該文應用了客戶端Socket會話(Session)概念;另一篇是美國人寫的,提出了多執行緒、分段接收資料包的技術方案,描述了多執行緒、非同步Socket的許多實現細節,該文堅定了筆者採用多執行緒和非同步方式處理Socket接收器的技術路線。第一個版本EMTASS 1.0(EMTASS,Extensible Multi-Thread Asynchronous Socket Server)於2006年初完成並投入使用。 今年暑假,筆者修改了原Socket接收伺服器程式碼,即EMTASS 1.1。最近,又按框架的可擴充套件性、可重用性等要求重新構思和設計了EMTASS,即EMTASS 2.0。下面的介紹共分六個部分:
>>
[前言]
[第1節][第2節][第3節][第4節][第5節][第6節]

1.1 總體思路

總體構思上,主要考慮多執行緒、非同步Socket和可擴充套件性三個方面。

1) 三個核心執行緒

在Internet環境下的Socket應用中,客戶端和網路容易出現異常,此時必須釋放異常退出的Socket資源。考慮到伺服器的高併發能力,一般採取包接收和處理分開的策略:將接收到的包新增到包佇列,然後處理佇列中的資料包。當然,偵聽遠端客戶端的連線請求可以用Socket的AcceptAsync()非同步方法(IOCP,I/O完成埠由此開始)。考慮到暫停、關閉同步操作,仍然用一個執行緒。這樣,清理資源、處理資料包、偵停客戶連線請求就是組成了EMTASS架構的三個核心執行緒,它們由.NET執行緒池統一管理:
  1. 客戶端連線偵聽執行緒 StartServerListen():
    迴圈偵聽遠端客戶端的Socket連線請求。如果存在,通過適當規範性判斷後建立該Socket的客戶端會話TSessionBase物件(實際上是該類的派生類物件),同時呼叫該會話物件的Socket非同步資料接收方法BeginReceive(),接收到的資料包存放在會話物件的包佇列中。當然,新增的TSessionBase物件將新增到會話佇列m_sessionTable(一個Dictionary<>泛型物件)中,該隊列表就是清理和處理執行緒的遍歷物件;
  2. 資料包處理執行緒 CheckDatagramQueue():迴圈檢測TSessonBase佇列中的會話物件,呼叫該物件的相關方法完成資料包解析、判斷型別、資料儲存等任務;
  3. 會話表檢測執行緒 CheckSessionTable():迴圈檢查會話表m_sessionTable中的各個會話物件,分步驟清理已經超時、無效或異常的會話物件,清理會話物件的緩衝區,釋放其Socket資源。

2) 非同步處理模式

.NET Framework中的Socket具有完整的非同步處理能力:偵聽後非同步接收(AcceptAsync())、資料非同步接收(BeginReceive())、資料非同步傳送(BeginSend())等。EMTASS框架採取了非同步接收和傳送方式,並封裝在TSessionBase類中。在EMTASS的版本1.0、1.1中,這些方法在主類TSocketServerBase中實現,顯然不符合類封裝原則。

3) 系統可擴充套件性

可擴充套件性主要考慮不同的業務處理邏輯和應用場景,即:資料包格式、資料儲存方法、資料庫伺服器等。框架EMTASS的可擴充套件性體現在類的泛型與抽象設計、方法虛擬和保護等方面:
  • 抽象類:會話基類TSessionBase、資料庫基類TDatabaseBase均是抽象類,分別提供了資料包分析與判斷、資料儲存的虛擬方法;
  • 泛型類:主要基類TSocketServerBase有TSessionBase、TDatabaseBase兩個泛型約束引數,可以根據這兩個抽象類的派生類產生具體的伺服器型別;
  • 方法抽象(abstract):TSessionBase的資料包分析方法AnalyzeDatagram()、TDatabaseBase的資料庫開啟方法Open()均是抽象的,必須在派生類中根據業務處理邏輯和資料庫型別重寫;
  • 方法保護(protected):與事件處理有關的方法、與業務處理邏輯相關的方法全部是protected方法,可以根據實際情況重寫。

1.2 類架構

1)主要類層次結構

EMTASS_ClassDiagram1.jpg
(圖1 主要類層次關係) 按應用類別分,EMTASS主要有四組類:Socket伺服器類、Session會話類、Database資料庫類和列舉型別。
  • Socket伺服器類
    • 伺服器泛型類 TSocketServerBase:該類包括了一個伺服器Socket物件、一個TDatabaseBase派生類物件、一個會話TSessionBase類派生物件的列表(Dictionary<>泛型物件),封裝了TDatabaseBase、TSessionBase的全部事件,提供統一的對外公開介面和事件;
    • 泛型引數:伺服器基類TSocketServerBase有兩個泛型引數:TSessionBase類和TDatabaseBase類,定製它們的派生類後可以確定泛型類的具體版本。
  • 客戶端會話類
    • 會話核心成員類 TSessionCoreInfo:是TSessionBase的基類,包括會話的核心欄位:登入時間、最近會話時間、IP地址、客戶端名、物件狀態等,是會話列表清單和事件引數的基類之一。該類的成員欄位全部是protected的,需要在派生類中賦予具體值;
    • 抽象會話類 TSessionBase:封裝了客戶端Socket、資料接收緩衝區、資料包緩衝區、資料包佇列等資料結構,包括了與客戶端通訊相關的全部方法:資料接收與傳送、資料包處理等。
  • 資料庫類
    • 抽象資料庫基類 TDatabaseBase:封裝了資料庫開啟與關閉、異常事件處理等方法,其中資料連線屬性DbConnection是虛屬性、資料庫開啟方法Open()是抽象方法,需要在派生類中重寫;
    • 基類TSqlServerBase:派生自TDatabaseBase,應用System.Data.SqlClient名稱空間中SqlServer相關型別重定義了資料庫連線屬性DbConnection、重寫了Open()方法;
    • 基類TOleDbDatabaseBase:派生自TDatabaseBase,應用System.Data.OleDb名稱空間中OleDb資料訪問相關型別重定義了基類的連線屬性DbConnection、重寫了Open()方法
  • 列舉型別
    • 會話狀態型別 TSessioinState:取4個值:Valid、Invalid、Shutdown和Closed。為Valid時表示會話是有效的,為Invalid時表示會話將被清理,為Shutdown時表示會話Socket正在解除安裝,為Closed時表示會話已經關閉、資源已經清理;
    • 會話斷開型別 TDisconnectType:取3個值:Normal、Timeout和Exception,分別表示正常連線、超時斷開、異常斷開。其中,超時表示最近兩次會話接收資料的時間超過約定的時限,防止某些會話長時間佔有資源。

2)事件引數型別結構圖

EMTASS_ClassDiagram2.jpg
(圖2 事件引數類層次關係) EMTASS框架的事件包括三類:第一,普通事件,如:伺服器啟動與停止;第二,異常事件,接收與傳送資料異常、資料庫連線或資料儲存異常等;第三,與會話相關事件,如:增加會話物件、接收到一個合法資料包等。異常與會話結合即是會話異常事件。通過泛型委託EventHandler可以定義類事件,其中的事件引數型別如下:
  • 異常事件引數類 TExceptionEventArgs:封裝了異常Exception物件的Message值;
  • 會話事件引數類 TSessionEventArgs:封裝了一個TSessionCoreInfo物件;
  • 會話異常引數類 TSessionExceptionEventArgs:派生自TSessionEventArgs,包括異常訊息欄位Message。
>>[前言][第1節][第2節][第3節][第4節][第5節][第6節]
下面介紹主要類TSocketServerBase和輔助類TSessionBase、TDatabaseBase中的主要實現方法。

2.1 TSocketServerBase類

該類包括了全部的對外介面和事件,主要實現前面介紹過的三個執行緒。

1) 執行緒池與同步訊號

.NET 提供了執行緒池方法 ThreadPool.QueueUserWorkItem() 自動將委託物件新增到系統執行緒池,見如下的實現程式碼:
    if (!ThreadPool.QueueUserWorkItem(this.StartServerListen)) return false;
if (!ThreadPool.QueueUserWorkItem(this.CheckDatagramQueue)) return false;
if (!ThreadPool.QueueUserWorkItem(this.CheckSessionTable)) return false;
其中, 客戶端連線請求偵聽方法StartServerListen()、資料包佇列檢查方法CheckDatagramQueue()和會話表檢查方法CheckSessionTable()均使用迴圈處理方式,迴圈條件是m_serverClosed為false。只有該類的Close()方法可以中斷這三個執行緒。在Close()方法中設定m_serverClosed為true終止執行緒的同時,還需要考慮執行緒退出的同步問題,此時使用手工事件訊號物件ManualResetEvent。參考如下資料包佇列檢查執行緒方法的程式碼:
private void CheckDatagramQueue(object state)
{
m_checkDatagramQueueResetEvent.Reset();

while (!m_serverClosed)
{
lock (m_sessionDictionary)
{
// ...其它程式碼
}
}

m_checkDatagramQueueResetEvent.Set();
}
上述程式碼是不安全的,一般要需要try{}finally{}保證事件訊號物件Reset()與Set()匹配。但EMTASS中的三個執行緒方法均有自己的異常處理方式,不會丟擲異常。下面是關閉伺服器方法Close()的主要程式碼。在設定變量了m_serverCloed為true後,使用了三個事件訊號等待,同步三個執行緒的正常終止。
private void Close()
{
if (m_serverClosed)
{
return;
}

m_serverClosed = true;
m_serverListenPaused = true;

m_checkServerListenResetEvent.WaitOne(); // 等待3個執行緒
m_checkSessionTableResetEvent.WaitOne();
m_checkDatagramQueueResetEvent.WaitOne();

// ...其它程式碼
}

2) 分步驟的清理執行緒

建立會話物件後,三種情況需要終止會話:1)關閉伺服器;2)會話異常;3)會話超時。第1種情況將強制終止會話,第2、3種情況需要清理執行緒終止會話並釋放其資源。為防止立即關閉Socket引發的異常,系統分3個步驟完成:1)標記該會話為Invalid,此時停止一切與該會話的處理操作;2)呼叫Shutdown()方法:Shutdown會話Socket,標記會話狀態為Shutdown;3)呼叫Close()方法:清除會話緩衝區和資料包佇列,釋放Socket資源,從會話表中刪除該物件。具體操作可以參考TSocketServerBase類中的CheckSessionTable()方法。

3) 事件傳遞與釋出

EMTASS框架的所有事件,包括TSessionBase類和TDatabaseBase類的事件,都通過伺服器類TSocketServerBase對外發布。在建立會話物件或資料庫物件時,直接傳遞其事件給TSocketServerBase的相同委託事件,見如下程式碼舉例:
    session.DatagramAccepted += new EventHandler(this.OnDatagramAccepted);
session.DatagramHandled += new EventHandler(this.OnDatagramHandled);
上述程式碼中,將TSessionBase派生類物件session的兩個事件直接繫結(使用+=方法)到當前TSocketServerBase物件上。具體實現程式碼可以參考TSocketServerBase的初始化方法Initiate()和新增會話物件方法AddSession()。

2.2 TSessionBase類

該抽象類包括客戶端Socket、資料接收緩衝區和資料包佇列等成員,封裝了所有與Socket通訊的方法。該類還包括資料包處理方法:資料包解析保護方法ResolveSessionBuffer()和資料包分析虛擬方法AnalyzeDatagram()。

1) 會話緩衝區和資料包佇列

TSessionBase包括兩個資料接收緩衝區和一個數據包佇列:
  • m_receiveBuffer緩衝區:接收客戶端Socket資料的緩衝區,如果資料包比該緩衝區長,Socket將自動(非同步)讀取幾次,每次用方法CopyToDatagramBuffer暫到資料包緩衝區m_datagramBuffer中;
  • m_datagramBuffer緩衝區:如果m_receiveBuffer接收了非完整的資料包,則使用該緩衝區暫存,直到獲得一個完整資料包。一般情況下,設定m_receiveBuffer大於資料包長度,則一次可以接收一個完整包,此時該緩衝區為空。此外,該緩衝區大小根據資料包的長度動態增長;
  • m_datagramQueue包佇列:位元組陣列的佇列(Queue<>泛型),儲存了當前會話的資料包(位元組陣列),等待處理執行緒分析與處理。在EMTASS 1.0與1.1中,該佇列結構封裝在TSocketServerBase內。這種設計增加了TSessionBase類與TSocketServerBase類的偶合程度。

2) 資料包解析方法ResolveSessionBuffer()

該方法是protected的,可以根據資料包結構與具體業務邏輯重寫程式碼。在TSessionBase類中實現的包組成規則是:開始字元是<結束字元是>。特別指出,Socket通訊中有兩個必須考慮的著名問題:
  • 資料包界限問題:資料包字串(位元組陣列)如何界限?在交通部的Socket通訊協議中,用<>分別作為一個包的開始與結束字元;
  • 資料包間斷與重疊問題:由於網路或裝置故障,一個數據包可能分兩次接收。另一種情況就是連續接收到多個數據包。第一種情況,需要先快取接收到的包直到一個完整的資料包。第二種情況,則需要根據包界限符號分解一個個的包。

3) 資料包分析方法AnalyzeDatagram()

該抽象方法是TSessionBase類必須重寫的方法,也是EMTASS框架擴充套件的主要介面,應該完成如下基本任務:
  • 判斷資料包的有效性與包型別;
  • 分解包中的各欄位資料;
  • 校驗包及其資料有效性;
  • 傳送確認訊息給客戶端(呼叫方法 SendDatagram());
  • 儲存包資料到資料庫中;
  • 如果資料包中存在客戶端名稱或編號,則填寫m_name欄位。

2.3 TDatabaseBase類

該抽象類定義了3個數據庫異常處理事件:DatabaseOpenException、DatabaseCloseExeption和DatabaseException,以及4個public方法:Open()、Close()、Clear()和Store()。其中,Open()是抽象方法,在派生類中可以增加自己的程式碼(見demo的實現部分),Close()方法關閉資料庫連線,Clear()方法在Close()中被呼叫——關閉資料庫前清理相關資源,虛方法Store()用於資料儲存。EMTASS框架給出了該基類的兩個派生類:TSqlServerBase和TOleDatabaseBase,可以滿足一般的資料庫應用需求。
>>[前言][第1節][第2節][第3節][第4節][第5節][第6節]

3.1 一般步驟

EMTASS框架的使用包括如下步驟:
  • 定製滿足需求的TSqlServerBase或TOleDatabaseBase派生類
    • 增加成員欄位,如:DbCommand、DbDataAdapter等;
    • 重寫TDatabaseBase類的虛擬方法Store(),編寫儲存資料包到資料庫中的實現程式碼;
    • 在TSessionBase的AnalyzeDatagram()方法中直接或間接呼叫Store()方法。
  • 定製滿足業務處理邏輯的TSessionBase派生類
    • 重寫ResolveSessionBuffer()方法,按照資料包規則提取緩衝區中的資料包文,並存儲到包佇列中;
    • 重寫AnalyzeDatagram()方法,按前面的要求增加功能。
  • 定製滿足需求的TSocketServerBase派生類
    • 定義TSocketSErverBase泛型類的派生類(該步可省略);
    • 建立泛型類的派生類物件,在建構函式中給出資料庫連線字串和TCP通訊埠;
    • 設定泛型類的派生類物件的最大資料包長度等引數;
    • 實現泛型類的派生類物件的相關事件處理方法。

3.2 TSocketServerBase的構造、屬性、方法和事件

泛型類TSocketServerBase提供了EMTASS框架的所有對外介面(屬性、方法和事件),包括內聯TSessionBase物件和TDatabaseBase物件的對外屬性和事件。

1) TSocketServerBase建構函式

有兩個過載版本,預設埠3130。考慮到可擴充套件性,必須給出資料庫連線串,見如下程式碼:
public TSocketServerBase(string dbConnectionString)
{
this.Initiate(dbConnectionString);
}

public TSocketServerBase(int tcpPort, string dbConnectionString)
{
m_servertPort = tcpPort;
this.Initiate(dbConnectionString);
}
建構函式中的方法Initiate()完成具體的初始化任務。

2) TSocketServerBase公共屬性

  • ServerPort:伺服器埠號,預設值為3130
  • Closed:伺服器已經關閉
  • ListenPaused:伺服器暫時停止客戶端連線請求
  • LoopWaitTime:Socket.Listen方法中的等待時間(ms),預設值為25ms
  • MaxDatagramSize:允許資料包的最大長度,預設值為1024K
  • MaxListenQueueLength:最大偵聽佇列長度,預設值為16
  • MaxReceiveBufferSize:允許資料包接收緩衝區的最大長度,預設值為16K
  • MaxSameIPCount:允許同地址IP的會話Socket個數,預設值為64
  • MaxSessionTableLength:允許最大會話表長度,預設值為1024
  • MaxSessionTimeout:允許最大的會話超時間隔(s),預設值為120s
  • ErrorDatagramCount:錯誤資料包個數
  • ReceivedDatagramCount:接收資料包個數
  • ServerExceptionCount:伺服器異常次數
  • SessionCount:當前會話個數
  • SessionExeptionCount:會話異常個數
  • SessionCoreInfoList:當前會話表資訊清單

3) TSocketServerBase公共方法

  • Start():啟動伺服器
  • Stop():關閉伺服器
  • PauseListen():暫停偵聽連線請求
  • ResumeListen():恢復偵聽連線請求
  • Dispose():關閉伺服器並釋放系統資源
  • CloseSession():關閉一個會話
  • CloseAllSessions():關閉全部會話
  • SendToSession():給一個會話傳送訊息
  • SendToAllSessions():給所有會話傳送訊息

4) TSocketServerBase事件

  • DatabaseCloseException:資料庫關閉異常
  • DatabaseException:資料庫異常
  • DatabaseOpenException:資料庫開啟異常
  • DatagramAccepted:接受了一個完整資料包
  • DatagramDelimiterError:資料包界限符錯誤
  • DatagramError:資料包錯誤
  • DatagramHandled:處理了一個數據包
  • DatagramOversizeError:資料包超長錯誤
  • ServerStarted:伺服器啟動後
  • ServerClosed:伺服器關閉後
  • ServerListenPaused:伺服器暫停連線請求後
  • ServerListenResumed:伺服器恢復連線請求後
  • ServerException:伺服器異常
  • SessionRejected:連線請求被拒絕
  • SessionConnected:建立一個會話連線
  • SessionDisConnected:斷開一個會話連線
  • SessionReceiveException:會話接收資料異常
  • SessionSendException:會話傳送資料異常
  • SessionTimeout:會話超時

3.3 下載包Demo介紹

下載包中包括EMTASS框原始碼和Demo。其中,VS2005的Demo解決方案檔案為EMTASS.sln,包含兩個專案:伺服器專案和客戶端專案。/bin/資料夾下的編譯檔案可直接執行:先啟動伺服器,然後執行客戶端。

1) 伺服器端Demo

伺服器端包括兩個部分:第一,接收伺服器窗體程式;第二,Access資料庫。伺服器端窗體程式包含如下實現:
  • TSessionBase派生類TTestSession:重寫了OnDatagramDelimiterError()和OnDatagramOversizeError()事件處理方法,重寫了資料包分析方法AnalyzeDatagram(),增加了一個自定義方法Store();
  • TDatabaseBase派生類TAccessDatabase:增加了一個OleDbCommmand欄位m_command,重寫了Open()方法和Store()方法。在重寫的Open()方法中,建立了m_command物件及其引數物件,給出了資料庫的Insert語句SQL程式碼。重寫的Store()方法中,將TSessionBase的IP、SessionName和資料包長度儲存到Access資料庫中。方法Store()將被TTestSession()的AnalyzeDatagram()方法呼叫;
  • TSocketServerBase<>物件:給出資料庫連線字串後,應用前面定義的兩個派生類建立泛型類物件,然後註冊該物件的事件實現方法。註冊的事件方法功能包括:顯示伺服器的如果計數情況,顯示伺服器執行狀態。
伺服器端的主要程式碼如下:
public partial class SocketServerDemo : Form
{
TSocketServerBase m_socketServer;

public SocketServerDemo()
{
InitializeComponent();
}

private void SocketServerDemo_Load(object sender, EventArgs e)
{
cb_maxDatagramSize.SelectedIndex = 1;

// 資料庫連線字串
string connStr = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source = DemoAccessDatabase.mdb;";

m_socketServer = new TSocketServerBase(connStr); // 伺服器物件
m_socketServer.MaxDatagramSize = 1024 * int.Parse(cb_maxDatagramSize.Text); // 包最大長度

this.AttachServerEvent(); // 附加伺服器全部事件
}

private void SocketServerDemo_FormClosing(object sender, FormClosingEventArgs e)
{
m_socketServer.Dispose(); // 關閉伺服器程序
}

private void AttachServerEvent()
{
m_socketServer.ServerStarted += this.SocketServer_Started;
m_socketServer.ServerClosed += this.SocketServer_Stoped;
m_socketServer.ServerListenPaused += this.SocketServer_Paused;
m_socketServer.ServerListenResumed += this.SocketServer_Resumed;
m_socketServer.ServerException += this.SocketServer_Exception;

m_socketServer.SessionRejected += this.SocketServer_SessionRejected;
m_socketServer.SessionConnected += this.SocketServer_SessionConnected;
m_socketServer.SessionDisconnected += this.SocketServer_SessionDisconnected;
m_socketServer.SessionReceiveException += this.SocketServer_SessionReceiveException;
m_socketServer.SessionSendException += this.SocketServer_SessionSendException;

m_socketServer.DatagramDelimiterError += this.SocketServer_DatagramDelimiterError;
m_socketServer.DatagramOversizeError += this.SocketServer_DatagramOversizeError;
m_socketServer.DatagramAccepted += this.SocketServer_DatagramReceived;
m_socketServer.DatagramError += this.SocketServer_DatagramrError;
m_socketServer.DatagramHandled += this.SocketServer_DatagramHandled;

m_socketServer.DatabaseOpenException += this.SocketServer_DatabaseOpenException;
m_socketServer.DatabaseCloseExcpetion += this.SocketServer_DatabaseCloseException;
m_socketServer.DatabaseExcpetion += this.SocketServer_DatabaseException;

m_socketServer.ShowDebugMessage += this.SocketServer_ShowDebugMessage;
}
//...其它程式碼
}
,>,>
下面是伺服器端Demo執行圖片 EMTASS_ServerDemo.jpg

2) 客戶端Demo

建立一個TcpClient物件,模擬遠端客戶端與伺服器通訊。下面是客戶端Demo執行圖片: EMTASS_ClientDemo.jpg
>>[前言][第1節][第2節][第3節][第4節][第5節][第6節]
  • 機器配置:雙核CPU E2140,主頻1.6G,RAM是1G(含顯示卡記憶體)。
  • 正確性測試
    • 測試方法:客戶端的資料包含有長度串,在伺服器端檢測比較,以此判斷資料包傳輸的正確性;
    • 測試情況:單機啟動伺服器,執行7個客戶端程式,其中3個是干擾源——連續做連線/斷開操作,另4個連續傳送資料包。執行約80分鐘檢查,伺服器平均每秒接收15個包,沒有發生異常,也沒有錯誤包,資料包佇列穩定在3以下。
  • 速度測試
    • 測試方法:單機執行伺服器,然後執行20個客戶端程式,用10-50ms的速度傳送資料包;
    • 測試情況:執行30分鐘檢查,伺服器平均每秒接收60個包,沒有出現數據包佇列顯著增長等情況。
  • 穩定性測試:客戶端Demo中包含一個連線後馬上斷開的連續操作,在這種情況下伺服器端沒有發現異常。此外,筆者在30個客戶端會話情況下執行伺服器1個小時,沒有發生伺服器端異常,但有個別客戶端異常退出。
  • 併發性測試:同時運行了30個客戶端Demo,沒有發生伺服器異常等現象,資料包佇列上限也不超過5。
  • 測試結論: 在一臺機器上做測試, EMTASS 2.0接收與處理正確,每秒可以處理60以上的資料包,執行穩定可靠,有較好的併發處理能力。測試中發現的主要問題如下:
    • CPU佔用率很大:顯然是三個執行緒的迴圈操作所引起的,在EMTASS1.0、1.1中,使用Thread.Sleep(m_waitTime)等待一段時間。本框架的設計目的是專用Socket伺服器,為提高吞吐能力省略了這個操作。如果必要,在以後版本中新增該功能;
    • 伺服器啟動後拒絕連線請求:特別是關閉後再啟動容易發生這種現象,多關閉啟動幾次後卻又恢復正常。筆者估計是伺服器m_serverSocket物件釋放資源不同步的原因;
    • 客戶端連線請求被拒絕:有時客戶端發生錯誤後,再連線時被拒絕。有兩種可能,第一種是如前所講的伺服器物件的問題;第二種是客戶端Socket接收、傳送和斷開時被阻塞,具體原因待分析。出現這種情況後,做多次連線/斷開操作還是可以連線伺服器。
顯然,這種測試環境和結果有待進一步驗證,但存在較大的改進空間。特別,資料包佇列最大值一般不超過5,表明伺服器接收到包後立即處理,併發效能比較好。
>>[前言][第1節][第2節][第3節][第4節][第5節][第6節]
本文介紹的EMTASS 2.0 是筆者一段工作的總結,也是學習基於.NET的類設計、元件設計、模式設計等的一個小結。筆者的目標就是不斷修改和完善,設計與實現一個可靠與穩定的、有良好可擴充套件性的和易於使用的Socket資料包接收伺服器框架。由於最初的程式碼和思路均來自他人的開源架構和設計構思,EMTASS也仿效一般開源做法:公佈原始碼和設計思路。 基於EMTASS 1.0的伺服器有連續執行30天完全正常的記錄,而框架EMTASS 2.0 雖然具有可擴充套件性,也進行了一般的測試,但沒有投入實際執行,需要時間檢驗和實踐驗證。當前,.NET 3.0及3.5 Framwwork提供的IOCP(完成埠)具有更好的非同步併發處理能力,筆者將結合新的執行平臺,完善與升級EMTASS,並公佈完善與升級計劃。如果有讀者使用EMTASS 2.0時發現問題,或有更好的建議或想法,請不吝指正。
>>[前言][第1節][第2節][第3節][第4節][第5節][第6節]
  • EMTASS 1.0, 2005年12月
  • EMTASS 1.1, 2008年09月
  • EMTASS 2.0, 2008年10月27日
    • 重設計了類名及類關係,增加了框架的可擴充套件性,重構了大部分程式碼
    • TSessionBase的資料包佇列取代TSocketServerBase的資料包佇列
    • TSessionBase中封裝了全部通訊方法
  • EMTASS 2.1, 2008年11月9日
    • 增加了緩衝區管理類BufferManager,管理兩個可重複使用的傳送/接收緩衝區。
    • TSocketServerBase增加了如下屬性:
      • ReceiveBufferSize:接收緩衝區大小(預設16K)
      • SendBufferSize:傳送緩衝區大小(預設16K)
      • CheckDatagramQueueTimeInterval:資料包處理執行緒Sleep時間間隔(預設:100ms)
      • CheckSessionTableTimeInterval:會話資源清理執行緒Sleep時間間隔(預設:100ms)
    • TSocketServerBase增加了若干建構函式,包括最大任務數和緩衝區大小
    • TSocketServerBase使用了Mutex互斥類,防止同機器建立兩個伺服器
    • TSessionBase在非同步接收/傳送完成後,呼叫IAsyncResult.AsyncWaitHandle.Close()方法
    • TSessionBase接收資料時,使用BufferManger公共緩衝區ReceivevBuffer
    • TSessionBase傳送資料時,如果據長度小於傳送緩衝區,則使用SendBuffer,否則申請位元組陣列傳送
    • 測試結果:10個干擾客戶端(100ms連續連開)與15個數據客戶端(3個100K/100ms、3個100K/50ms、4個100K/20ms、2個1M/1s、2個1M/500ms、1個1M/10s),CPU佔用率為70-90%,速度為40/s,無錯誤包,直接顯示的連線數為30-50之間