基於ACE Proactor框架下高併發、大容量吞吐程式設計既最近的一個產品開發總結
Reactor與 Proactor
基本概念
在高效能的I/O設計中,有兩個比較著名的模式Reactor和Proactor模式,其中Reactor模式用於同步I/O,而Proactor運用於非同步I/O操作。
在比較這兩個模式之前,我們首先的搞明白幾個概念,
-
- 什麼是阻塞和非阻塞
阻塞和非阻塞是針對於程式在訪問資料的時候,根據IO操作的就緒狀態來採取的不同方式,說白了是一種讀取或者寫入操作函式的實現方式。
阻塞方式下讀取或者寫入函式將一直等待。
非阻塞方式下,讀取或者寫入函式會立即返回一個狀態值。
-
- 什麼是同步和非同步
同步和非同步是針對應用程式和核心的互動而言的。
同步指的是使用者程式觸發IO操作並等待或者輪詢的去檢視IO操作是否就緒。
非同步是指使用者程式觸發IO操作以後便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知。
一般來說I/O模型可以分為:同步阻塞,同步非阻塞,非同步阻塞,非同步非阻塞。
讓我們來看一下每種不同I/O模型的具體描述
-
- 同步阻塞 IO:
在此種方式下,使用者程式在發起一個IO操作以後,必須等待IO操作的完成,只有當真正完成了IO操作以後,使用者程式才能執行。JAVA傳統的IO模型屬於此種方式!
-
- 同步非阻塞IO:
在此種方式下,使用者程式發起一個IO操作以後邊可返回做其它事情,但是使用者程式需要時不時的詢問IO操作是否就緒,這就要求使用者程式不停的去詢問,從而引入不必要的CPU資源浪費。其中目前JAVA的NIO就屬於同步非阻塞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()者"資料還沒準備好,你稍後再試".。
在非阻塞非同步呼叫中,稍有不同。呼叫函式在立即返回時,還告訴呼叫者,這次請求已經開始了。系統會使用另外的資源或者執行緒來完成這次呼叫操作,並在完成的時候知會呼叫者(比如通過回撥函式)。拿Windows的ReadFile()或者POSIX的aio_read()來說,呼叫它之後,函式立即返回,作業系統在後臺同時開始讀操作。
在以上三種IO形式中,非阻塞非同步是效能最高、伸縮性最好的。搞清楚了以上概念以後,我們再回過頭來看看,Reactor模式和Proactor模式。
此文詳細的闡述了基於TCP高效能的GOLDEN資料伺服器模組的設計以及解決方案 ,我們在文章的後面就不再提及阻塞式的方案了,因為阻塞式I/O實在是缺少可伸縮性,效能也達不到高效能伺服器的要求。
兩種IO多路複用方案:Reactor和 Proactor
一般情況下,I/O複用機制需要事件分離器(event demultiplexor ). 事件分離器的作用,就是將那些讀寫事件源分發給各讀寫事件的處理者,就像送快遞的在樓下喊:誰的什麼東西送了,快來拿吧。開發人員在開始的時候需要在事件分離器那裡註冊感興趣的事件,並提供相應的事件處理器(event handlers),或者是回撥函式;事件分離器在適當的時候會將請求的事件分發給這些handler或者回撥函式。
涉及到事件分離器的兩種模式稱為:Reactor和Proactor。Reactor模式是基於同步I/O的,而Proactor模式是和非同步I/O相關的。在Reactor模式中,事件分離者等待某個事件或者是應用或者是某個操作的狀態發生(比如檔案描述符可讀寫,或者是socket可讀寫),事件分離者就把這個事件傳給事先註冊的事件處理器或者事件處理函式或者回撥函式,由後者來做實際的讀寫操作。
而在Proactor模式中,事件處理器(或者由事件分離器代為)直接發起一個非同步讀寫操作(相當於請求),而實際的工作是由作業系統來完成的。發起時,需要提供的引數包括用於存放讀到資料的快取區,讀的資料大小,或者用於存放外發資料的快取區,以及這個請求完後的回撥函式等資訊。事件分離器得知了這個請求,它默默等待這個請求的完成,然後轉發完成事件給相應的事件處理器或者事件處理函式或者回撥。舉例來說,在Windows上事件處理器投遞了一個非同步IO操作(稱有overlapped的技術),事件分離器等IOCompletion事件完成 ,這種非同步模式的典型實現是基於作業系統底層非同步API的,所以我們可稱之為“系統級別”的或者“真正意義上”的非同步,因為具體的讀寫是由作業系統代勞的。
舉另外個例子來更好地理解Reactor與Proactor兩種模式的區別。這裡我們只關注read操作,因為write操作也是差不多的。下面是Reactor的做法:
-
某個事件處理器宣稱它對某個socket上的讀事件很感興趣;
-
事件分離者等著這個事件的發生;
-
當事件發生了,事件分離器被喚醒,這負責通知先前那個事件處理器;
-
事件處理器收到訊息,於是去那個socket上讀資料了. 如果需要,它再次宣稱對這個socket上的讀事件感興趣,一直重複上面的步驟;
下面再來看看真正意義的非同步模式Proactor是如何做的:
-
事件處理器直接投遞發一個讀操作(當然,作業系統必須支援這個非同步操作)。這個時候,事件處理器根本不關心讀事件,它只管發這麼個請求,它魂牽夢縈的是這個讀操作的完成事件。這個事件處理器很拽,發個命令就不管具體的事情了,只等著別人(系統)幫他搞定的時候給他回個話。
-
事件分離器等著這個讀事件的完成(比較下與Reactor的不同);
-
當事件分離器默默等待完成事情到來的同時,作業系統已經在一邊開始幹活了,它從目標讀取資料,放入使用者提供的快取區中,最後通知事件分離器,這個事情我搞完了;
-
事件分離器通知之前的事件處理器: 你吩咐的事情搞定了;
-
事件處理器這時會發現想要讀的資料已經乖乖地放在他提供的快取區中,想怎麼處理都行了。如果有需要,事件處理器還像之前一樣發起另外一個讀操作,和上面的幾個步驟一樣。
現行做法
開源C++開發框架ACE(Adaptive Communication Enviromen) 提供了大量平臺獨立的底層併發支援類(執行緒、互斥量等).同時在更高一層它也提供了獨立的幾組C++類,用於實現Reactor及Proactor模式。 儘管它們都是平臺獨立的單元,但他們都提供了不同的介面.
ACE Proactor在MS-Windows上無論是效能還在健壯性都更勝一籌,這主要是由於Windows提供了一系列高效的底層非同步API。
不幸的是,並不是所有作業系統都為底層非同步提供健壯的支援。舉例來說,許多Unix系統就有麻煩.ACE中的Proactor在Unix上是使用Posix標準實現的非同步操作,Posix中有一個AIO,Proactor使用AIO實現非同步傳輸。但Linux在2.6以前版本中不支援AIO,而在2.6版本以後,部分支援AIO。就因為這個部分支援,所以,Posix的子類不能正常工作。因此,ACE Reactor可能是Unix系統上更合適的解決方案.正因為系統底層的支援力度不一,為了在各系統上有更好的效能,開發者不得不維護獨立的好幾份程式碼:為Windows準備的ACE Proactor以及為Unix系列提供的ACE Reactor。
就像我們提到過的,真正的非同步模式需要作業系統級別的支援。由於事件處理器及作業系統互動的差異,為Reactor和Proactor設計一種通用統一的外部介面是非常困難的。這也是設計通行開發框架的難點所在。
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框架類:
-
ACE_Service_Handler 在Proactor框架中建立事件處理器所用的目標類 。
-
ACE_Handler ACE_Service_Handler的父類,定義了通過ACE_Proactor框架處理非同步I/O完成事件所需要的介面。
-
ACE_Asynch_Read_Stream 用於在已經連線的TCP/IP socket上發起讀操作的I/0工廠類。
-
ACE_Asynch_Write_Stream 用於在已經連線的TCP/IP socket上發起寫操作的I/0工廠類。
-
Result 每個I/O工廠類都把Result定義為嵌在自己內部的類,用以儲存該工廠發起的每個操作的結果。所有的Result類都從ACE_Asynch_Result派生,並且增加了專用於它們所針對的I/O型別的資料和方法。因為每個非同步I/O操作的發起和完成都是分離的、不同的事情,需要有一種機制來“記住”操作的引數,並且連同結果一起吧這些引數轉交給完成處理器。
設定事件處理器併發起I/O
當TCP連線開啟時,我們應該把新socket的控制程式碼傳給事件處理器物件,在這個例子中是golden_aio_handler。把控制程式碼放在事件處理器裡是有益的,原因如下:
-
它是socket的生命期一個方便的控制點,因為它是連線工廠的目標。
-
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設計中我們採用了了執行緒池的方法。
執行緒池模型有兩種變種,每種都有不同的效能特徵:
-
半同步/半非同步模型。在這種模型中,一個偵聽會非同步的接收請求,並在某個佇列中緩衝它們。另外一組工作者執行緒負責同步地處理這些請求。
-
領導者/跟隨著模型。在這種模型總,有一個執行緒是領導者,其餘執行緒是線上程池中的跟隨者。當請求到達時,領導者會拾取它,並從跟隨者中選取一個新的領導者,然後繼續處理該請求。因此,在這種模型中,接收請求的執行緒池就是處理它的執行緒。
領導者/跟隨者模型中,只用了一組執行緒等待新請求,並處理請求。一個執行緒被選作領導者,阻塞在“到來的請求源”上,當請求到達時,領導者執行緒首先獲取請求,把某個跟隨者提升為領導者,然後繼續處理所收到的請求。新領導者在請求源上等待新的請求,與此同時舊領導者會處理剛剛收到的請求,一旦就領導者完成處理,它就會作為跟隨者執行緒回到執行緒池的末尾。
領導者/跟隨者模型的一個優點是效能得到了提高,因為不用進行執行緒間的上下文切換。但同時這種模型也是複雜的。在程式中單個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 Server中golden_user、golden_authorize、golden_startup、golden_prot、golden_system_info、golden_dir_visitor、golden_mirror_sender、golden_mirror_receiver採用單體例項模式。相關單體模式的概念,請自行參考相關手冊和書籍。
如何新增一個介面應用
Goldensdk模組
-
開啟goldensdk.h標頭檔案,根據介面型別找到合適的位置,新增介面的宣告、介面的描述註釋。
-
開啟goldensdk.cpp檔案,新增介面的空實現。
-
開啟goldensdk.def檔案,新增介面名稱。
-
按照步驟完成Goldenserver模組
-
在goldensdk.cpp文實現已經新增的空介面。
Goldenserver模組
-
在golden_message_protocol.h中新增自定義的報文資料結構定義和報文訊息ID , 命名規則如下GOLDEN_PACK_XXX、GOLDEN_PACK_XXX_RESULT、MESSAGE_XXX、MESSAGE_XXX_RESULT。
-
在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);
-
在golden_message_protocol.cpp中新增用來計算報文資料結構大小的函式實現和整編\解編資料流的函式實現。
-
在golden_message_protocol.cpp中新增全部變數_gstring中的內容,用來描述報文內容。
-
在goldeserver.h中的golden_aio_handler類中新增處理報文的成員函式宣告。命名規則如下:int msg_xxx() 、int msg_xxx_result() .
-
在goldeserver.cpp中新增處理報文的成員函式的空實現golden_aio_handler:: msg_xxx(), golden_aio_handler:: msg_xxx_result() .
-
在goldeserver.cpp的 int golden_aio_handler::handle_msg_pack()函式中新增報文處理項。
-
實現f步驟中定義的成員函式空實現。
參考文獻
-
C++網路程式設計卷一--- 運用ACE和模式消除複雜性
-
C++網路程式設計卷二--- 基於ACE和框架的系統化複用
-
ACE程式設計師指南--- 網路與系統程式設計的實用設計模式
-
設計模式:可複用物件導向軟體的基礎
相關文章
- 併發程式設計概念大總結--乾貨程式設計
- 一、產品開發設計的坑
- 關於Java併發程式設計的總結和思考Java程式設計
- Java併發程式設計實戰總結 (一)Java程式設計
- 基於DFSS設計開發新能源汽車電子產品
- 高併發大容量NoSQL解決方案探索SQL
- Java 併發程式設計學習總結Java程式設計
- 併發程式設計——基礎概念(一)程式設計
- 嵌入式產品開發設計需要考慮的問題總結
- 【Java併發程式設計】併發程式設計大合集-值得收藏Java程式設計
- Oracle EBS 基於Host併發程式的開發Oracle
- 【我的產品觀】開發wangEditor一年總結
- 高併發網路程式設計程式設計
- 一個SAP開發人員的年終總結:程式設計與游泳程式設計
- Java多執行緒與併發程式設計總結(一)Java執行緒程式設計
- Go 併發程式設計 - Goroutine 基礎 (一)Go程式設計
- 如何設計一個高可用、高併發秒殺系統
- 面試題:如何設計一個高併發系統?面試題
- 一個故事搞懂Java併發程式設計Java程式設計
- 高併發程式設計-AQS深入解析程式設計AQS
- 網際網路產品開發流程總結
- 基於執行緒與基於事件的併發程式設計之爭執行緒事件程式設計
- [併發程式設計]-關於 CAS 的幾個問題程式設計
- Golang併發程式設計基礎Golang程式設計
- 併發程式設計基礎(上)程式設計
- 併發程式設計基礎(下)程式設計
- Java併發程式設計基礎Java程式設計
- Go併發程式設計基礎Go程式設計
- Java併發程式設計——基礎知識(一)Java程式設計
- 做一個有產品思維的研發:開發
- 產品設計總結第一篇
- 鴻蒙程式設計江湖:併發程式設計基礎與鴻蒙中的任務併發鴻蒙程式設計
- 併發程式設計模型小結程式設計模型
- 【高併發】併發程式設計到底應該學什麼?一張圖秒懂!!程式設計
- 響應式程式設計在 SAP 標準產品 UI 開發中的一個實踐程式設計UI
- 【Java併發程式設計】synchronized相關面試題總結Java程式設計synchronized面試題
- c++11併發程式設計歷程(15):併發設計以及併發設計資料結構的思考C++程式設計資料結構
- 大廠員工,手把手教你開發一個高併發、高可用的營銷活動