【原創】基於ACE Proactor框架下高併發、大容量吞吐程序設計既最近的一個產品開發總結

ReactorProactor

基本概念

在高性能的I/O設計中,有兩個比較著名的模式ReactorProactor模式,其中Reactor模式用於同步I/O,而Proactor運用於異步I/O操做。程序員

       在比較這兩個模式以前,咱們首先的搞明白幾個概念,編程

    • 什麼是阻塞和非阻塞

阻塞和非阻塞是針對於進程在訪問數據的時候,根據IO操做的就緒狀態來採起的不一樣方式,說白了是一種讀取或者寫入操做函數的實現方式。windows

阻塞方式下讀取或者寫入函數將一直等待。設計模式

非阻塞方式下,讀取或者寫入函數會當即返回一個狀態值。緩存

    • 什麼是同步和異步

同步和異步是針對應用程序和內核的交互而言的。服務器

同步指的是用戶進程觸發IO操做並等待或者輪詢的去查看IO操做是否就緒。網絡

異步是指用戶進程觸發IO操做之後便開始作本身的事情,而當IO操做已經完成的時候會獲得IO完成的通知。數據結構

通常來講I/O模型能夠分爲:同步阻塞,同步非阻塞,異步阻塞,異步非阻塞多線程

讓咱們來看一下每種不一樣I/O模型的具體描述併發

    •    同步阻塞 IO

   在此種方式下,用戶進程在發起一個IO操做之後,必須等待IO操做的完成,只有當真正完成了IO操做之後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式!

    •    同步非阻塞IO:

在此種方式下,用戶進程發起一個IO操做之後邊可返回作其它事情,可是用戶進程須要時不時的詢問IO操做是否就緒,這就要求用戶進程不停的去詢問,從而引入沒必要要的CPU資源浪費。其中目前JAVANIO就屬於同步非阻塞IO

    •    異步阻塞IO

此種方式下,應用發起一個IO操做之後,不等待內核IO操做的完成,等內核完成IO操做之後會通知應用程序,這其實就是同步和異步最關鍵的區別,同步必須等待或者主動的去詢問IO是否完成,那麼爲何說是阻塞的呢?由於此時是經過select系統調用來完成的,而select函數自己的實現方式是阻塞的,而採用select函數有個好處就是它能夠同時監聽多個文件句柄,從而提升系統的併發性!

    •    異步非阻塞IO:

此種方式下,用戶進程只須要發起一個IO操做而後當即返回,等IO操做真正的完成之後,應用程序會獲得IO操做完成的通知,此時用戶進程只須要對數據進行處理就行了,不須要進行實際的IO讀寫操做,由於真正的IO讀取或者寫入操做已經由內核完成了。目前Java中尚未支持此種IO模型。   

阻塞型I/O意味着控制權只有到調用操做結束後纔會回到調用者手裏.結果調用者被阻塞了,這段時間了作不了任何其它事情。 更鬱悶的是,在等待IO結果的時間裏,調用者所在線程此時沒法騰出手來去響應其它的請求,這真是太浪費資源了。拿read() 操做來講吧,調用此函數的代碼會一直僵在此處直至它所讀的socket緩存中有數據到來。

相比之下,非阻塞同步是會當即返回控制權給調用者的。調用者不須要等等,它從調用的函數獲取兩種結果:要麼這次調用成功進行了;要麼系統返回錯誤標識告訴調用者當前資源不可用,你再等等或者再試一次看吧。好比read()操做,若是當前socket無數據可讀,則當即返回EWOULBLOCK/EAGAIN,告訴調用read()"數據還沒準備好,你稍後再試".

在非阻塞異步調用中,稍有不一樣。調用函數在當即返回時,還告訴調用者,此次請求已經開始了。系統會使用另外的資源或者線程來完成此次調用操做,並在完成的時候知會調用者(好比經過回調函數)。拿WindowsReadFile()或者POSIXaio_read()來講,調用它以後,函數當即返回,操做系統在後臺同時開始讀操做。

在以上三種IO形式中,非阻塞異步是性能最高、伸縮性最好的。搞清楚了以上概念之後,咱們再回過頭來看看,Reactor模式和Proactor模式。

此文詳細的闡述了基於TCP高性能的GOLDEN數據服務器模塊的設計以及解決方案 ,咱們在文章的後面就再也不說起阻塞式的方案了,由於阻塞式I/O實在是缺乏可伸縮性,性能也達不到高性能服務器的要求。

兩種IO多路複用方案:ReactorProactor

通常狀況下,I/O複用機制須要事件分離器(event demultiplexor ). 事件分離器的做用,就是將那些讀寫事件源分發給各讀寫事件的處理者,就像送快遞的在樓下喊:誰的什麼東西送了,快來拿吧。開發人員在開始的時候須要在事件分離器那裏註冊感興趣的事件,並提供相應的事件處理器(event handlers),或者是回調函數;事件分離器在適當的時候會將請求的事件分發給這些handler或者回調函數。

涉及到事件分離器的兩種模式稱爲:ReactorProactorReactor模式是基於同步I/O的,而Proactor模式是和異步I/O相關的。在Reactor模式中,事件分離者等待某個事件或者是應用或者是某個操做的狀態發生(好比文件描述符可讀寫,或者是socket可讀寫),事件分離者就把這個事件傳給事先註冊的事件處理器或者事件處理函數或者回調函數,由後者來作實際的讀寫操做。

而在Proactor模式中,事件處理器(或者由事件分離器代爲)直接發起一個異步讀寫操做(至關於請求)而實際的工做是由操做系統來完成的。發起時,須要提供的參數包括用於存放讀到數據的緩存區,讀的數據大小,或者用於存放外發數據的緩存區,以及這個請求完後的回調函數等信息。事件分離器得知了這個請求,它默默等待這個請求的完成,而後轉發完成事件給相應的事件處理器或者事件處理函數或者回調。舉例來講,在Windows上事件處理器投遞了一個異步IO操做(稱有overlapped的技術),事件分離器等IOCompletion事件完成 ,這種異步模式的典型實現是基於操做系統底層異步API的,因此咱們可稱之爲「系統級別」的或者「真正意義上」的異步,由於具體的讀寫是由操做系統代勞的。

舉另外個例子來更好地理解ReactorProactor兩種模式的區別。這裏咱們只關注read操做,由於write操做也是差很少的。下面是Reactor的作法:

  1. 某個事件處理器宣稱它對某個socket上的讀事件很感興趣;

  2. 事件分離者等着這個事件的發生;

  3. 當事件發生了,事件分離器被喚醒,這負責通知先前那個事件處理器;

  4. 事件處理器收到消息,因而去那個socket上讀數據了. 若是須要,它再次宣稱對這個socket上的讀事件感興趣,一直重複上面的步驟;

下面再來看看真正意義的異步模式Proactor是如何作的:

  1. 事件處理器直接投遞發一個讀操做(固然,操做系統必須支持這個異步操做)。這個時候,事件處理器根本不關心讀事件,它只管發這麼個請求,它魂牽夢縈的是這個讀操做的完成事件。這個事件處理器很拽,發個命令就無論具體的事情了,只等着別人系統)幫他搞定的時候給他回個話。

  2. 事件分離器等着這個讀事件的完成(比較下與Reactor的不一樣);

  3. 當事件分離器默默等待完成事情到來的同時,操做系統已經在一邊開始幹活了,它從目標讀取數據,放入用戶提供的緩存區中,最後通知事件分離器,這個事情我搞完了;

  4. 事件分離器通知以前的事件處理器: 你吩咐的事情搞定了;

  5. 事件處理器這時會發現想要讀的數據已經乖乖地放在他提供的緩存區中,想怎麼處理都行了。若是有須要,事件處理器還像以前同樣發起另一個讀操做,和上面的幾個步驟同樣。

現行作法

開源C++開發框架ACEAdaptive Communication Enviromen 提供了大量平臺獨立的底層併發支持類(線程、互斥量等).同時在更高一層它也提供了獨立的幾組C++類,用於實現ReactorProactor模式。 儘管它們都是平臺獨立的單元,但他們都提供了不一樣的接口.

ACE ProactorMS-Windows上不管是性能還在健壯性都更勝一籌,這主要是因爲Windows提供了一系列高效的底層異步API

不幸的是,並非全部操做系統都爲底層異步提供健壯的支持。舉例來講,許多Unix系統就有麻煩. ACE中的ProactorUnix上是使用Posix標準實現的異步操做,Posix中有一個AIOProactor使用AIO實現異步傳輸。但Linux2.6之前版本中不支持AIO,而在2.6版本之後,部分支持AIO。就由於這個部分支持,因此,Posix的子類不能正常工做。所以,ACE Reactor多是Unix系統上更合適的解決方案.正由於系統底層的支持力度不一,爲了在各系統上有更好的性能,開發者不得不維護獨立的好幾份代碼:Windows準備的ACE Proactor以及爲Unix系列提供的ACE Reactor

就像咱們提到過的,真正的異步模式須要操做系統級別的支持。因爲事件處理器及操做系統交互的差別,爲ReactorProactor設計一種通用統一的外部接口是很是困難的。這也是設計通行開發框架的難點所在。

ACE Proactor 框架

怎樣發送和接收數據

ACEProactor框架包含了一組高度相關的類,其數量相對較多,我在進行如下描述的時候不可能按照順序討論它們,而又不進行提早引用。到最後我會描述完全部這些類。下面這些類給出了ACE Proactor框架的各個類以及它們之間的關係。能夠把這個圖1-1看成描述ACE Proactor框架實際應用的範本。注意:類名中以ACE_開始的類名稱是ACE Procator框架中包含的類,而以golden_開始的類名稱是實際應用範本提供的類。

下面的代碼聲明瞭一個類,它所完成的基本工做是處理接收和發送數據。

1.1 ACE Proactor框架中的類

#include "ace/Asynch_IO.h"

class golden_aio_handler : public ACE_Service_Handler

{

public :

golden_aio_handler (golden_aio_acceptor *acc = 0) ;

virtual void open ( ACE_HANDLE new_handle,

ACE_Message_Block &message_block ) ;

virtual void handle_read_stream(

const ACE_Asynch_Read_Stream::Result &result);

virtual void handle_write_stream(

const ACE_Asynch_Write_Stream::Result &result);

private:

ACE_Asynch_Read_Stream reader_;

ACE_Asynch_Write_Stream writer_;

} ;

這段代碼首先包含了一些必需的頭文件,以引入這個例子使用的ACE Proactor框架類:

  1. ACE_Service_Handler Proactor框架中建立事件處理器所用的目標類 。

  2. ACE_Handler ACE_Service_Handler的父類,定義了經過ACE_Proactor框架處理異步I/O完成事件所須要的接口。

  3. ACE_Asynch_Read_Stream 用於在已經鏈接的TCP/IP socket上發起讀操做的I/0工廠類。

  4. ACE_Asynch_Write_Stream 用於在已經鏈接的TCP/IP socket上發起寫操做的I/0工廠類。

  5. Result 每一個I/O工廠類都把Result定義爲嵌在本身內部的類,用以保存該工廠發起的每一個操做的結果。全部的Result類都從ACE_Asynch_Result派生,而且增長了專用於它們所針對的I/O類型的數據和方法。由於每一個異步I/O操做的發起和完成都是分離的、不一樣的事情,須要有一種機制來「記住」操做的參數,而且連同結果一塊兒吧這些參數轉交給完成處理器。

設置事件處理器併發起I/O

TCP鏈接打開時,咱們應該把新socket的句柄傳給事件處理器對象,在這個例子中是golden_aio_handler。把句柄放在事件處理器裏是有益的,緣由以下:

    1. 它是socket的生命期一個方便的控制點,由於它是鏈接工廠的目標。

    2. I/O操做最有可能從這個類發起。

在使用ACE_Proactor框架的異步鏈接創建類時golden_aio_handler::open()掛鉤方法會在新鏈接創建時被調用。下面是咱們程序中的open()掛鉤:

void

golden_aio_handler::open(ACE_HANDLE new_handle, ACE_Message_Block &)

{

this->handle(new_handle);

//打開異步讀寫

reader_.open (*this, new_handle, 0, proactor ());

writer_.open (*this, new_handle, 0, proactor ());

//準備讀的緩衝區

ACE_NEW_NORETURN(mblk_, ACE_Message_Block (SIZEOF_HEADER_WITH_CRC));

if (reader_.read (*mblk_,SIZEOF_HEADER_WITH_CRC) <0)

{

delete this ;

}

}

在一開始,咱們使用繼承而獲得的ACE_Handler::handle()方法保存新socket的句柄。該方法把句柄存儲在一個方便的地方,以便在析構函數~golden_aio_handler()訪問或者用於其餘用途。這是在這個類中實現的socket句柄生命期管理的一部分。

要發起I/O,必須初始化所需的I/O工廠對象。在存儲了socket句柄以後,open()方法會初始化reader_writer_ I/O工廠對象,爲發起I/O操做作準備。兩個類的open()方法都是同樣的:

int open (ACE_Handler &handler,

ACE_HANDLE handle = ACE_INVALID_HANDLE,

const void *completion_key = 0,

ACE_Proactor *proactor = 0);

第一個參數表示工廠對象所發起的操做的完成事件處理器裏。當經過工廠對象發起的I/O操做完成時,ACE_Proactor框架會回調這個對象。這也是爲何該處理器對象叫作完成事件處理器的緣由。在咱們的程序中,golden_aio_handler對象是ACE_Handler的後代,便是讀操做也是寫操做的完成事件處理器,因此*this被用做處理器參數。handle是新傳入的socket句柄,completion_key參數只適用於windows默認傳入0便可,proactor參數會傳入一個在進程範圍的ACE_Procator單體對象。

程序中的open()掛鉤方法所作的最後一件事情,是調用ACE_Asynch_Read_Stream::read()方法,從而在新的socket上發起一個讀操做。ACE_Asynch_Read_Stream::read()函數以下:

int read (ACE_Message_Block &message_block,

size_t num_bytes_to_read,

const void *act = 0,

int priority = 0,

int signal_number = ACE_SIGRTMIN);

爲傳輸指定一個ACE_Message_Block ,使得緩衝區管理變得更爲容易,由於能夠利用ACE_Message_Block的各類能力,以及它與ACE的其餘部分的集成。在發起讀操做時,數據會被讀入開始於數據塊的寫指針處在的塊中,由於要被讀取的數據將被寫入塊中。

完成I/O操做

ACE_Proactor框架是基於事件的框架。I/O工廠登記「每一個操做」與「該操做完成時應回調的完成事件處理器」之間創建關聯。當讀取完成時,ACE_Proactor框架會調用ACE_Handler::handle_read_stream()掛鉤方法:

void golden_aio_handler::handle_read_stream(

const ACE_Asynch_Read_Stream::Result &result)

{ ACE_Asynch_Read_Stream::Result &result

if (!result.success () || result.bytes_transferred () == 0)

delete this;

else if (result.bytes_transferred () < result.bytes_to_read ())

{

if (reader_.read (*mblk_, result.bytes_to_read () - result.bytes_transferred ()) < 0)

delete this ;

}

else if (mblk_->length () == SIZEOF_HEADER_WITH_CRC)

handle_msg_header();

else

{

if (handle_msg_pack()<0)

delete this ;

}

}

傳入的ACE_Asynch_Read_Stream::Result指向的是用於保存讀取操做結果的對象。每一個I/O工廠類都會定義本身的Result類,即用於保存每一個操做發起時所用的參數,又用於保存操做的結果。

若是讀操做讀取了任何數據,處理接收read到的報文數據包用handle_msg_pack函數,而後發起一個寫操做,把數據處理結果返回給對端。當寫操做完成時,ACE_Proactor框架調用下面的handle_write_stream方法:

void golden_aio_handler::handle_write_stream(const ACE_Asynch_Write_Stream::Result &result)

{

if(reader_.read (*mblk_,SIZEOF_HEADER_WITH_CRC) < 0)

delete this;

}

無論寫操做是否成功完成,在該操做中使用的消息塊都會釋放。若是socket出了問題,先前發起的讀操做也會完成並出錯,而handle_read_stream()會清理對象和socket句柄。

1-2給出了本程序事件序列。

創建鏈接

ACE提供裏兩個工廠類,用於經過ACE_Proactor框架前攝式地創建TCP/IP鏈接:

ACE_Asynch_Acceptor , 用於發起被動的鏈接創建

ACE_Asynch_Connector , 用於發起主動的鏈接創建

1.2 ACE Proactor異步回調序列圖

當使用其中一個類創建TCP/IP鏈接時,ACE_Proactor框架會建立一個從ACE_Service_Handler派生的事件服務處理器,好比golden_aio_handler,用以處理新鏈接。ACE_Service_Handler類是

ACE_Proactor框架中全部用異步方式鏈接的服務的基類,從ACE_Handler派生,因此服務類也能夠處理在服務中發起的I/O操做的完成.

ACE_Asynch_Acceptor是一個至關容易使用的類,它的一個掛鉤方法是一個protected虛方法:make_handler()Proactor框架調用這個方法獲取一個ACE_Service_Handler對象,用覺得新鏈接提供服務。下面的代碼說明了這種狀況:

golden_aio_handler * golden_aio_acceptor::make_handler (void)

{

///來一個鏈接,就新增一個句柄。在線程池中處理

golden_aio_handler *ih;

ACE_NEW_RETURN (ih, golden_aio_handler (this), 0);

if (clients_.insert (ih) == -1)

{

delete ih ;

return NULL ;

}

return ih;

}

return 0 ;

}

ACE_Proactor完成多路分離器

ACE_Proactor類負責驅動ACE_Proactor框架的完成處理,這個類等待完成事件的發生、把這些事件多路分離給相關聯的完成事件處理器,並分派每一個完成處理器上適當的掛鉤方法。所以,要讓異步I/O完成事件處理器得以發生----不管是I/O仍是鏈接創建----Golden Server中都必須運行前攝器的時間循環。這一般很簡單,只須要把下面的代碼插入到應用中就能夠了:

int golden_aio::svc()

{

ACE_Proactor::instance()->proactor_run_event_loop ();

return 1 ;

}

能夠經過兩種方式來使用ACE_Proactor,如上所示的程序代碼instance(),做爲單體來使用。也能夠經過實例化一個或多個實例來使用。這個能力被用於在一個進程中支持多個前攝器。以下代碼所示,這是應用於鏡像發送和鏡像接收的前攝器

int golden_mirror_sender::svc()

{

proactor_sender_->proactor_run_event_loop ();

return 1 ;

}

int golden_mirror_receiver::svc()

{

proactor_recviver_->proactor_run_event_loop ();

return 1 ;

}

各類操做系統上的異步I/O設施會有很大的不一樣,爲了在全部這些系統上維持統一的接口和編程方法,ACE_Proactor類使用了Bridge模式來維持靈活性和可擴展性,同時還使得ACE_Proactor框架可以使用不一樣的異步I/O實現。

ACE_WIN32_Proactor類是Windows上的ACE_Proactor實現。使用了I/O完成端口進行完成事件檢測。在初始化異步操做工廠時,I/O句柄與前攝器的I/O完成端口被關聯在一塊兒。在這種實現中,windows下的GetQueuedCompletionStatus()函數負責執行事件循環,以下程序代碼

int golden_server::create_proactor()

{

ACE_Proactor::instance()->close_singleton();

impl_ = new ACE_WIN32_Proactor(0,1);

ACE_Proactor::instance(new ACE_Proactor(impl_,1),1) ;

return 0;

}

int golden_server::create_proactor_mirror_recviver()

{

ACE_NEW_RETURN(impl_mirror_recviver_, ACE_WIN32_Proactor(0,1),-1);

ACE_NEW_RETURN(mirror_recviver_proactor_, ACE_Proactor(impl_mirror_recviver_,1),-1);

return 0;

}

int golden_server::create_proactor_mirror_sender()

{

ACE_NEW_RETURN(impl_mirror_sender_, ACE_WIN32_Proactor(0,1),-1);

ACE_NEW_RETURN(mirror_sender_proactor_, ACE_Proactor(impl_mirror_sender_,1),-1);

return 0;

}

線程池

大多數網絡服務器都被設計成能同時處理多個客戶請求。使用反應式事件處理、多個進程和多個線程。在構建多線程服務器時,咱們擁有多種選擇,包括:爲每一個請求派生一個新線程、爲每一個鏈接/會話派生一個新線程、預先派生一池受管線程,也就是建立一個線程池。在Golden Server設計中咱們採用了了線程池的方法。

線程池模型有兩種變種,每種都有不一樣的性能特徵:

  1. 半同步/半異步模型。在這種模型中,一個偵聽會異步的接收請求,並在某個隊列中緩衝它們。另一組工做者線程負責同步地處理這些請求。

  2. 領導者/跟隨着模型。在這種模型總,有一個線程是領導者,其他線程是在線程池中的跟隨者。當請求到達時,領導者會拾取它,並從跟隨者中選取一個新的領導者,而後繼續處理該請求。所以,在這種模型中,接收請求的線程池就是處理它的線程。

領導者/跟隨者模型中,只用了一組線程等待新請求,並處理請求。一個線程被選做領導者,阻塞在「到來的請求源」上,當請求到達時,領導者線程首先獲取請求,把某個跟隨者提高爲領導者,而後繼續處理所收到的請求。新領導者在請求源上等待新的請求,與此同時舊領導者會處理剛剛收到的請求,一旦就領導者完成處理,它就會做爲跟隨者線程回到線程池的末尾。

領導者/跟隨者模型的一個優勢是性能獲得了提升,由於不用進行線程間的上下文切換。但同時這種模型也是複雜的。在程序中單個ACE_Task封裝了線程池中的全部線程。

class golden_aio :public ACE_Task<ACE_SYNCH>

{

public:

golden_aio(int number_of_connection)

/// ACE_TASK的虛擬方法。用來啓動svc

virtual int open (void * = 0);

///初始化

virtual int init(u_short ,ACE_Proactor* );

///結束並關閉

virtual int fini();

/// Run by a daemon thread to handle deferred processing

virtual int svc (void);

};

int golden_aio::open (void * )

{

return activate (THR_NEW_LWP | THR_JOINABLE |THR_INHERIT_SCHED ,number_of_thread_);

}

int golden_aio::svc()

{

ACE_Proactor::instance()->proactor_run_event_loop ();

return 1 ;

}

每一個線程啓動時,首先會經過調用activate(),將任務轉化爲運行在一個或多個線程中的主動對象。主動對象執行任務的svc()掛鉤方法。每一個線程在執行proactor_run_event_loop ()調用GetQueuedCompletionStatus ()。 若是沒有消息到達線程會阻塞在GetQueuedCompletionStatus ()函數上,直到有消息到來那麼有一個線程便成了領導者線程。若是領導者線程能很快的處理完全部事情,領導者線程會再次進入等待狀態。若是領導者線程不能立刻處理完,則從跟隨者線程中指定一個新的領導者線程,本身去處理事件,再也不當領導者。

單體模型

Golden Servergolden_usergolden_authorizegolden_startupgolden_protgolden_system_infogolden_dir_visitorgolden_mirror_sendergolden_mirror_receiver採用單體實例模式。相關單體模式的概念,請自行參考相關手冊和書籍。

如何添加一個接口應用

Goldensdk模塊

  1. 打開goldensdk.h頭文件,根據接口類型找到合適的位置,添加接口的聲明、接口的描述註釋。

  2. 打開goldensdk.cpp文件,添加接口的空實現。

  3. 打開goldensdk.def文件,添加接口名稱。

  4. 按照步驟完成Goldenserver模塊

  5. goldensdk.cpp文實現已經添加的空接口。

Goldenserver模塊

  1. golden_message_protocol.h中添加自定義的報文數據結構定義和報文消息ID , 命名規則以下GOLDEN_PACK_XXXGOLDEN_PACK_XXX_RESULTMESSAGE_XXXMESSAGE_XXX_RESULT

  2. golden_message_protocol.cpp中添加用來計算報文數據結構大小的函數聲明和整編\解編數據流的函數聲明。命名規則以下:sizeof_GOLDEN_PACK_XXX() sizeof_GOLDEN_PACK_XXX_RESULT()int operator<<(ACE_OutputCDR &cdr , const GOLDEN_PACK_XXX &spack) int operator<<(ACE_OutputCDR &cdr , const GOLDEN_PACK_XXX_RESUL &spack);

  3. golden_message_protocol.cpp中添加用來計算報文數據結構大小的函數實現和整編\解編數據流的函數實現。

  4. golden_message_protocol.cpp中添加所有變量_gstring中的內容,用來描述報文內容。

  5. goldeserver.h中的golden_aio_handler類中添加處理報文的成員函數聲明。命名規則以下:int msg_xxx() int msg_xxx_result() .

  6. goldeserver.cpp中添加處理報文的成員函數的空實現golden_aio_handler:: msg_xxx(), golden_aio_handler:: msg_xxx_result() .

  7. goldeserver.cppint golden_aio_handler::handle_msg_pack()函數中添加報文處理項。

  8. 實現f步驟中定義的成員函數空實現。

參考文獻

  1. C++網絡編程卷一--- 運用ACE和模式消除複雜性

  2. C++網絡編程卷二--- 基於ACE和框架的系統化複用

  3. ACE程序員指南--- 網絡與系統編程的實用設計模式

  4. 設計模式:可複用面向對象軟件的基礎

相關文章
相關標籤/搜索