《電子技術(shù)應(yīng)用》
您所在的位置:首頁(yè) > 通信與網(wǎng)絡(luò) > 設(shè)計(jì)應(yīng)用 > 一種適用于通信軟件的事件多路分解框架
一種適用于通信軟件的事件多路分解框架
陶偉業(yè)1,顏昭治1,徐海水2,梁碧允1
1.廣東工業(yè)大學(xué) 計(jì)算機(jī)學(xué)院,廣東 廣州510090;2.廣東工業(yè)大學(xué) 網(wǎng)絡(luò)信息與現(xiàn)代教育技術(shù)中心,
摘要: 介紹了ACE反應(yīng)器框架的核心設(shè)計(jì)與實(shí)現(xiàn),揭示了該框架對(duì)事件實(shí)施多路分解和分派的機(jī)制。
Abstract:
Key words :

摘   要: 介紹了ACE反應(yīng)器框架的核心設(shè)計(jì)與實(shí)現(xiàn),揭示了該框架對(duì)事件實(shí)施多路分解和分派的機(jī)制。
關(guān)鍵詞: ACE反應(yīng)器  設(shè)計(jì)模式  框架

  在分布式系統(tǒng)、特別是服務(wù)器的事件驅(qū)動(dòng)型應(yīng)用中,必須隨時(shí)準(zhǔn)備同時(shí)處理多個(gè)服務(wù)請(qǐng)求。許多傳統(tǒng)的應(yīng)用程序在處理諸如網(wǎng)絡(luò)連接這樣的多個(gè)I/O端口服務(wù)時(shí),往往借助于多進(jìn)程模型或多線程模型。這種方法在需要同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的服務(wù)器程序中相當(dāng)流行。但是,在一些系統(tǒng)中,進(jìn)程和線程的創(chuàng)建開(kāi)銷(xiāo)和維護(hù)代價(jià)非常大。其不足主要表現(xiàn)在以下方面:
  (1)由于CPU之間的上下文切換、同步和數(shù)據(jù)移動(dòng),使得線程方法效率不高。
  (2)線程方法不適用于所有的操作系統(tǒng),并且不是所有的操作系統(tǒng)都提供可移植的線程語(yǔ)義。
  (3)相似的事件處理代碼在每一個(gè)新的工程中,由開(kāi)發(fā)者面對(duì)許多復(fù)雜細(xì)節(jié)的情況下被重復(fù)地開(kāi)發(fā)出來(lái),并且這些程序在移植時(shí)困難重重。
  (4)在制訂進(jìn)程或線程策略進(jìn)行并發(fā)服務(wù)器的優(yōu)化時(shí),如果不考慮像CPU的數(shù)目這樣的可用資源,實(shí)際上往往降低了程序的執(zhí)行效率。
  本文描述ACE(自適配通信環(huán)境)反應(yīng)器框架的設(shè)計(jì)和實(shí)現(xiàn)。該框架可以有效克服上述不足。它實(shí)現(xiàn)了反應(yīng)器(Reactor)模式[1],該模式將事件多路分解和分派機(jī)制從服務(wù)中對(duì)指示事件的與應(yīng)用有關(guān)的處理分離了出去。應(yīng)用的每個(gè)服務(wù)可由一個(gè)或多個(gè)方法組成,并由一個(gè)單獨(dú)的事件處理器代表;事件處理器負(fù)責(zé)分派服務(wù)特有的請(qǐng)求。在ACE的反應(yīng)器模式的實(shí)現(xiàn)中,事件處理器分派由ACE_Reactor對(duì)象完成。
1  ACE反應(yīng)器框架類(lèi)
  這一部分簡(jiǎn)要介紹反應(yīng)器框架(ACE_Reactor)的類(lèi)。圖1顯示了該框架中相關(guān)類(lèi)之間最為重要的關(guān)系。

  (1)ACE_Time_Value類(lèi):主要用于ACE Reactor I/O超時(shí)和定時(shí)器設(shè)置。
  (2)ACE_Event_Handler類(lèi):抽象類(lèi),其接口提供的掛鉤方法是ACE_Reactor回調(diào)的目標(biāo)。大多數(shù)通過(guò)ACE開(kāi)發(fā)的應(yīng)用事件處理器都是該類(lèi)的后代。
  (3)ACE_Timer_Queue類(lèi):抽象類(lèi),定義定時(shí)器隊(duì)列的能力和接口。
  (4)ACE_Reactor類(lèi):提供一個(gè)接口,用來(lái)在Reactor框架中管理事件處理器登記,并執(zhí)行事件循環(huán)來(lái)驅(qū)動(dòng)事件檢測(cè)、多路分解和分派。
  依照反應(yīng)器模式,這些類(lèi)扮演了事件基礎(chǔ)設(shè)施層和應(yīng)用層二種角色。其中,擔(dān)當(dāng)應(yīng)用層角色的是ACE_Event_Handle類(lèi)。
2  ACE反應(yīng)器框架的核心實(shí)現(xiàn)
  實(shí)現(xiàn)ACE反應(yīng)器框架的關(guān)鍵是運(yùn)用反應(yīng)器(Reactor)模式。反應(yīng)器模式中有五個(gè)主要的組成部分:(1)操作系統(tǒng)提供的句柄:用于標(biāo)識(shí)網(wǎng)絡(luò)連接或打開(kāi)的文件之類(lèi)的事件源,事件源產(chǎn)生指示事件并對(duì)其進(jìn)行排隊(duì)。(2)同步事件多路分解器:是一個(gè)函數(shù),如select()等,它是事件多路分解器的核心。(3)事件處理程序:定義一個(gè)或多個(gè)鉤子方法組成的接口。(4)具體事件處理程序:從事件處理程序接口繼承,并實(shí)現(xiàn)應(yīng)用所特定的服務(wù)。(5)反應(yīng)器:定義了一個(gè)接口,允許應(yīng)用程序登記或刪除事件處理程序及其相關(guān)的句柄,并運(yùn)行應(yīng)用程序的事件循環(huán)。反應(yīng)器使用同步事件多路分解器等待在句柄集上發(fā)生指示事件。
應(yīng)用程序開(kāi)發(fā)者只需要負(fù)責(zé)具體事件處理程序,并在反應(yīng)器上予以注冊(cè),應(yīng)用程序就可以簡(jiǎn)單地重用反應(yīng)器的多路分解和分配機(jī)制了。反應(yīng)器模式引入的結(jié)構(gòu)實(shí)現(xiàn)回調(diào)的方法是:反應(yīng)器等待指示事件,多路分解這些事件給具體事件處理程序,然后向具體事件處理程序分派相應(yīng)的鉤子方法。下面介紹反應(yīng)器框架的實(shí)現(xiàn)。
2.1 定義事件處理程序?qū)ο?/strong>
  在面向?qū)ο髴?yīng)用中,將事件處理程序與句柄結(jié)合起來(lái)的方法是建立一個(gè)事件處理對(duì)象。ACE反應(yīng)器框架定義了不同類(lèi)型的各種對(duì)象及相應(yīng)的鉤子方法,這種由具體事件處理對(duì)象來(lái)分派的多接口策略更具可擴(kuò)展性。下面簡(jiǎn)化的C++抽象基類(lèi)即是在ACE反應(yīng)器框架中用來(lái)產(chǎn)生這種類(lèi)型的對(duì)象:
  class ACE_Event_Handler{
  public://由反應(yīng)器分派的用以處理各種具體事件處理程序的鉤子方法
  virtual int handle_input(HANDLE handle)=0;//輸入事件
  virtual int handle_outpur(HANDLE handle)=0;//輸出事件
  virtual int handle_timeout(const ACE_Time_Value&)=0;
             //超時(shí)事件
  virtual HANDLE get_handle( ) const=0;}//用于返回I/O句柄的鉤子方法
2.2 定義反應(yīng)器接口
  下面是一個(gè)簡(jiǎn)化的ACE_Reactor類(lèi),它是ACE反應(yīng)器框架中實(shí)現(xiàn)反應(yīng)器模式的關(guān)鍵類(lèi)。應(yīng)用程序使用該反應(yīng)器接口登記或刪除事件處理程序及其相關(guān)句柄,并調(diào)用應(yīng)用程序的事件循環(huán)。通常用單件[5]訪問(wèn)反應(yīng)器接口,因?yàn)橐粋€(gè)應(yīng)用程序中有一個(gè)反應(yīng)器就夠了。
  class ACE_Reactor {
  public:
  virtual int register_handler(ACE_Event_Handler*event_
  handler,ACE_Reactor_Mask masks);
         //登記具體事件處理器
  virtual int register_handler(ACE_Sig_Set sigset,ACE_Event_
  Handler*event_handler,ACE_Reactor_Mask masks);
        //該事件處理器與信號(hào)處理有關(guān)
  virtual int schedule_timer(ACE_Event_Handler*event_
  handler,ACE_Time_Value time);//登記一個(gè)事件處理器
                  //它將在用戶規(guī)定的時(shí)間后被執(zhí)行
  virtual int remove_handler(HANDLE h,ACE_Reactor_
    Mask masks);//移除具體事件處理器
  void handle_events(ACE_Time_Value*timeout=0);
              //啟動(dòng)反應(yīng)器的事件循環(huán)處理
  static Reactor*instance( );//返回反應(yīng)器單體實(shí)例
  private:
  ACE_Reactor_Impl implementation;}//反應(yīng)器的具體實(shí)現(xiàn)
  在上面的類(lèi)中,ACE_Reactor_Mask是個(gè)自定義類(lèi)型:
  typedef unsigned long ACE_Reactor_Mask
  它一般取以下枚舉類(lèi)型的值,用來(lái)標(biāo)志不同類(lèi)型的事件:
  enum{
  READ_MASK=(1 << 0),
  WRITE_MASK=(1 << 1),
  EXCEPT_MASK=(1 <<2 )
  ……}
  ACE_Reactor類(lèi)是應(yīng)用程序用以訪問(wèn)ACE反應(yīng)器框架的公共接口。橋接模式[5]使ACE_Reactor接口與它的ACE_Reactor_Impl子類(lèi)實(shí)現(xiàn)耦合。在不同OS平臺(tái)上,該子類(lèi)的實(shí)現(xiàn)也不相同。但是,ACE_Reactor接口提供的方法的名字和總的功能保持不變。這種統(tǒng)一性源于ACE_Reactor設(shè)計(jì)的模塊性,該設(shè)計(jì)還增強(qiáng)了反應(yīng)器的可重用性、可移植性和可維護(hù)性。
2.3 反應(yīng)器中多路分解和分派的實(shí)現(xiàn)
  通常,除了調(diào)用同步事件多路分解器等待句柄集發(fā)生指示事件外,反應(yīng)器實(shí)現(xiàn)還要維護(hù)一個(gè)多路分解表。該表是個(gè)管理者,包含一個(gè)格式為<句柄,事件處理程序,指示事件類(lèi)型>的三元組,這使得所激活的句柄與激活該句柄的指示事件類(lèi)型、所激活的句柄與該句柄所關(guān)聯(lián)的事件處理器三者之間清晰地聯(lián)系在一起??捎肔inux操作系統(tǒng)為例來(lái)闡述這一點(diǎn),因?yàn)樵贚inux中,I/O句柄是連續(xù)的整數(shù),這使得句柄值成為多路分解表數(shù)組的索引。
  在具體實(shí)現(xiàn)中,ACE反應(yīng)器類(lèi)的私有部分有一個(gè)包含上述多路分解表的對(duì)象handler_rep_,該對(duì)象存放著一個(gè)實(shí)現(xiàn)句柄到具體事件處理器映射的表,它是下面所示類(lèi)的一個(gè)實(shí)例:
  class ACE_Select_Reactor_Handler_Repository{
  public://尋找與handle相關(guān)聯(lián)的具體事件處理器
  ACE_Event_Handler*find(ACE_HANDLE handle,size_t
  *index_p=0);
       //使得具體事件的句柄、指示標(biāo)志綁定在一起
  Int bind(ACE_HANDLE,ACE_Event_Handler*,ACE_
  Reactor_Mask):
  size_t size(void) const;//返回表中所綁定的具體事件處理器的個(gè)數(shù)
   private://盛裝一個(gè)事件處理器ACE_Event_Handler和其相關(guān)
       //聯(lián)的ACE_HANDLE句柄
  ACE_Event_Tuple*event_handler-;}
  class ACE_Event_Tuple{
  public:
  ACE_HANDLE handle_;
  ACE_Event_Handler*event_handler_;}
  當(dāng)具體事件處理器調(diào)用register_handler( )方法向Reactor類(lèi)登記時(shí),將調(diào)用方法handler_rep_.bind (handle,event_handler,mask),把這個(gè)“三元組”置于handler_rep_對(duì)象中。
  在ACE中,用包裝器外觀類(lèi)ACE_Handle_Set來(lái)封裝句柄集,用一個(gè)實(shí)現(xiàn)了迭代模式的類(lèi)ACE_Handle_Set_
  Iterator來(lái)迭代該句柄集中的句柄。一旦反應(yīng)器進(jìn)入主入口點(diǎn)方法handle_events( ),應(yīng)用程序?qū)⒗盟磻?yīng)性地實(shí)現(xiàn)事件循環(huán)。當(dāng)有指示事件發(fā)生時(shí),反應(yīng)器多路分解與分派(即回調(diào))的方式為:該框架把反應(yīng)器應(yīng)處理的事件分成三種不同的類(lèi)型,其中一類(lèi)為I/O事件,由方法dispatch_io_
handlers( )加以處理。下面以處理常見(jiàn)應(yīng)用中的I/O事件為例說(shuō)明反應(yīng)器的多路分解和分派的實(shí)現(xiàn)。
先定義一個(gè)函數(shù)指針:
  typedef int (ACE_Event_Handler∷?鄢ACE_EH_PTMF) (ACE_HANDLE);
  此函數(shù)指針是實(shí)現(xiàn)具體服務(wù)分派的基礎(chǔ),因?yàn)榉磻?yīng)器通過(guò)函數(shù)指針決定了應(yīng)調(diào)用handle_?鄢( )方法中的哪一種。
  再定義一個(gè)函數(shù):
  ACE_Reactor_impl∷dispatch_io_set( ACE_Handle_Set&
  dispatch_mask,ACE_EH_PTMF callback)
  {  ACE_Handle_Set_Iterator handle_iter(dispatch_mask);
               //對(duì)特定句柄集中的句柄進(jìn)行迭代
  While(handle=handle_iter( )!=ACE_INVALID_HANDLE)
   { ACE_Event_Handler //找出被激活句柄相關(guān)聯(lián)
              //的事件處理器
  event_handler=this->handler_rep_.find(handle);
                //利用函數(shù)指針對(duì)反映器進(jìn)行回調(diào)
  (event_handler->*callback)(handle);}
  }
  上述函數(shù)對(duì)dispatch_mask句柄集中的句柄執(zhí)行循環(huán)檢測(cè)。一旦某句柄被激活,即找出該句柄所關(guān)聯(lián)的ACE_Event_Handler對(duì)象,然后執(zhí)行該對(duì)象所分派的方法,而具體方法由callback傳遞。
3  運(yùn)用反應(yīng)器框架的示例
  以常見(jiàn)的登錄服務(wù)器為例。登錄服務(wù)器使用由兩個(gè)具體事件處理程序,即登錄接受器和登錄連接器實(shí)現(xiàn)的單件反應(yīng)器。下面的代碼框架實(shí)現(xiàn)了登錄服務(wù)器示例中的具體事件處理程序。其中:My_Accept_Handler類(lèi)(登錄接受器)用來(lái)被動(dòng)地建立連接,My_Input_Handler類(lèi)(登錄連接器)提供與應(yīng)用有關(guān)的服務(wù)。
  class My_Input_Handler:public ACE_Event_Handler{
  public:
  int handle_input(ACE_HANDLE){ //回調(diào),以處理任何到來(lái)的連接
     ……具體代碼略……}
  private:
  ACE_SOCK_Stream peer_;}//流對(duì)象,用于讀寫(xiě)
  class My_Accept_Handler:public ACE_Event_Handler{
  public:
  My_Accept_Handler(ACE_Addr &addr){this->open(addr);}
  int open(ACE_Addr &addr) {//打開(kāi)接受器,監(jiān)聽(tīng)客戶連接
  peer_acceptor.open(addr);}
  int handle_input(ACE_HANDLE handle){
        //重載handle_input( )方法
  ……代碼略,客戶請(qǐng)求連接,則建立一個(gè)處理器來(lái)處理這個(gè)連接……}
  private:
  ACE_SOCK_Accepto peer_acceptor;} //用以被動(dòng)接受連接的接受器
  int main(int argc,char*argv[]){
  ACE_INET_Addr addr(PORT_NO);//建立一個(gè)用來(lái)接受連接的地址
                  //建立一個(gè)接受事件處理器用以自動(dòng)偵聽(tīng)客戶連接
  My_Accept_Handler*eh=new My_Accept_Handler(addr);
                 //向Reactor登記,以使在有連接請(qǐng)求時(shí)實(shí)現(xiàn)回調(diào)
  ACE_Reactor∷instance( )->register_handler(eh,
  ACE_Event_Handler∷ACCEPT_MASK);
       while(1) //運(yùn)行事件循環(huán)
  ACE_Reactor∷instance()->handle_events();}
4  總  結(jié)
  本文對(duì)實(shí)現(xiàn)ACE反應(yīng)器框架的核心源碼進(jìn)行了分析,揭示了該框架使多路分解和分派機(jī)制與應(yīng)用定義的事件處理策略相分離的方法。最后的實(shí)例表明,通過(guò)封裝許多復(fù)雜的功能,該框架可簡(jiǎn)潔、正確、可移植和高效地進(jìn)行事件驅(qū)動(dòng)型網(wǎng)絡(luò)化應(yīng)用的開(kāi)發(fā),從而使網(wǎng)絡(luò)化開(kāi)發(fā)者能夠?qū)W⒂趹?yīng)用所特有的服務(wù)。
參考文獻(xiàn)
1   Schmidt D C,Stal M,Rohnert H et al.Pattern-Oriented  Software Architecture:Patterns for Concurrent and Networked Objects.West Sussex:Wiley&Sons,2000
2   Schmidt D C,Huston S D.C++ Network Programming,Volume 2:Systematic Reuse with ACE and Frameworks.  Massachusetts:Addison-Wesley,2002
3   Schmidt D C.Reactor:An Object Behavioral Pattern for Demultiplexing and Despatching Handles for Synchronous Events.http://www.cs.wustl.edu/~doc/pspdfs/Reactor.pdf,2004-12-05
4   Schmidt D C,Pyarali I.The Design and Use of the ACE Reactor:An Objiect-Oriented Framework for Event  Demutiplexing.http://www.cs.wustl.edu/~doc/pspdfs/Reactor.pdf,2004-12-05
5   Gamma E,Helm R,Johnson R et al.Design Patterns:Elements of Reusable Object-Oriented Software.Massachusetts:Addison-Wesley,1995
6   Schmidt D.C,Huston S D.C++ Network Programming,Volume 1:Mastering Complexity with ACE and Patterns.Massachusetts:Addison-Wesley,2001

此內(nèi)容為AET網(wǎng)站原創(chuàng),未經(jīng)授權(quán)禁止轉(zhuǎn)載。