![ACE開發(fā)指南(初級)_第1頁](http://file4.renrendoc.com/view/711dcd7cb0d5db2f89e2291e1e9d89d7/711dcd7cb0d5db2f89e2291e1e9d89d71.gif)
![ACE開發(fā)指南(初級)_第2頁](http://file4.renrendoc.com/view/711dcd7cb0d5db2f89e2291e1e9d89d7/711dcd7cb0d5db2f89e2291e1e9d89d72.gif)
![ACE開發(fā)指南(初級)_第3頁](http://file4.renrendoc.com/view/711dcd7cb0d5db2f89e2291e1e9d89d7/711dcd7cb0d5db2f89e2291e1e9d89d73.gif)
![ACE開發(fā)指南(初級)_第4頁](http://file4.renrendoc.com/view/711dcd7cb0d5db2f89e2291e1e9d89d7/711dcd7cb0d5db2f89e2291e1e9d89d74.gif)
![ACE開發(fā)指南(初級)_第5頁](http://file4.renrendoc.com/view/711dcd7cb0d5db2f89e2291e1e9d89d7/711dcd7cb0d5db2f89e2291e1e9d89d75.gif)
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
CJ-000032-RAS-001 開發(fā)指南MadebyExpertTeam上海超捷系統(tǒng)集成有限公司,199777/82 ACE開發(fā)指南(初級)文檔信息作者鄭明智創(chuàng)建日期200版本1.0部門名稱開發(fā)部修訂文檔歷史記錄日期版本說明作者2001.0Reactor鄭明智2006-12-271.1增加Proactor的內容鄭明智2006-12-281.2增加ACETask的內容鄭明智
目錄1. 介紹 11.1 目的 11.2 文檔協(xié)定 11.3 閱讀者建議 11.4 術語說明 11.5 翻譯約定 21.6 相關資料 21.7 參考文獻 21.8 補充說明 22. ACE簡介及環(huán)境搭建 32.1 ACE簡介 32.2 本指南的主要內容 32.3 獲取ACE 42.4 編譯ACE 42.4.1 為什么要編譯ACE 42.4.2 在Window上編譯 42.4.3 在Linux上編譯 52.5 前行的路標 63. ACEReactor框架 63.1 Reactor(反應器)框架 63.1.1 ACE_Event_Handler(事件處理器) 73.1.2 ACE_Reactor 93.2 Acceptor(接受器)-Connector(連接器)框架 113.2.1 ACE_Svc_Handler(服務處理器) 123.2.2 ACE_Acceptor 143.2.3 ACE_Connector 153.3 ACEReactorServer(Demo) 173.3.1 需求 173.3.2 實現 173.3.3 ACE工具類 253.3.4 Server改進 263.4 ACEReactorClient(Demo) 293.4.1 需求 293.4.2 實現I 293.4.3 使用超時機制發(fā)送消息 333.4.4 實現II 333.5 前行的路標 374. ACEProactor框架 384.1 Proactor(前攝器)框架 384.1.1 異步I/O工廠類 394.1.2 ACE_Handler(完成處理器) 414.1.3 ACE_Message_Block 424.1.4 ACE_Proactor 434.2 前攝式Acceptor-Connector框架 444.2.1 ACE_Service_Handler 454.2.2 ACE_Asynch_Acceptor 464.2.3 ACE_Asynch_Connector 464.3 既生Proactor,何生Reactor(二者的應用范圍) 464.4 ACEProactorServer(Demo) 474.4.1 需求 474.4.2 實現 474.5 前行的路標 545. ACETask框架 555.1 我們的新需求 555.2 Task(任務)框架 555.3 ACE_Message_Queue 565.4 ACE_Task 595.5 Demo(ReactorClient的改寫) 615.5.1 需求 615.5.2 實現 615.6 基本的線程安全性 715.6.1 互斥體(Mutex) 715.6.2 守衛(wèi)(Guard) 755.7 前行的路標 766. 總結 777. 常見問題 77CJ-000028-RAS-001 開發(fā)指南MadebyExpertTeam介紹目的本指南作為使用ACE框架開發(fā)應用程序的參考,以期能夠對使用ACE框架的同事有所幫助。文檔協(xié)定本文檔的書寫遵循公司定義的文檔規(guī)范。本指南寫作時,ACE最新的穩(wěn)定版本為5.5版。本指南中觀點和代碼并不保證適用于后續(xù)的ACE版本。本指南旨在幫助新手入門,如果您已對ACE有一定使用經驗并想更深入了解ACE,建議您閱讀ACE的相關書籍。閱讀者建議本指南假定閱讀者有C++的開發(fā)經驗和通信程序的開發(fā)經驗,文檔中C++及Socket等開發(fā)知識及相關概念不再贅述。術語說明C/SClient/Server客戶端/服務器架構Client客戶端Server服務器ACE自適配通信環(huán)境(AdaptiveCommunicationEnvironment)Reactor反應器,高效的事件多路分離和分派提供可擴展的面向對象框架Proactor前攝器aio異步I/OAsynchronousI/O異步I/OEpollLinux從2.6開始支持的異步事件I/O技術翻譯約定Method/Function方法NestedClass內部類Callback回調HookMethod掛鉤方法Handle句柄Template模板Daemon守護Override/Overwrite覆蓋Overload重載相關資料ACE-5.5.zipMagicC++3.5參考文獻[1]ACE程序員指南–網絡與系統(tǒng)編程的實用設計模式[2]C++網絡編程(卷1)運用ACE和模式消除復雜性[3]C++網絡編程(卷2)基于ACE和框架的系統(tǒng)化復用[4]ACE自適配通信環(huán)境中文技術文檔–中篇ACE程序員教程[5]ACEAPI列表[6]ACE源代碼補充說明[1]本文檔中提到的資料,工具,代碼都放在了相關資料放在\\0\upload\開發(fā)部\技術組\ACE目錄下。[2]如果您發(fā)現本指南有錯誤或者疏漏,請通知\o"發(fā)郵件給作者"作者修改,以使本指南能夠更好的幫助大家。ACE簡介及環(huán)境搭建ACE簡介ACE自適配通信環(huán)境(AdaptiveCommunicationEnvironment)是面向對象的框架和工具包,它為通信軟件實現了核心的并發(fā)和分布式模式。ACE包含的多種組件可以幫助通信軟件的開發(fā)獲得更好的靈活性、效率、可靠性和可移植性。ACE中的組件可用于以下幾種目的:并發(fā)和同步進程間通信(IPC)內存管理定時器信號文件系統(tǒng)管理線程管理事件多路分離和處理器分派連接建立和服務初始化軟件的靜態(tài)和動態(tài)配置、重配置分層協(xié)議構建和流式框架分布式通信服務:名字、日志、時間同步、事件路由和網絡鎖定,等等。上面是比較官方的介紹??傊?,如果你想用C++實現一個通信程序,而你又不想糾纏于Windows/Linux等各平臺不同的Socket細節(jié),那就可以考慮ACE框架。ACE經過10余年的開發(fā),已經不僅僅是通信的包,還實現了很多其它功能比如內存管理,文件系統(tǒng)管理等,可以滿足您大多數開發(fā)的需要。而且ACE是被全世界很多項目采用的可以稱的上是電信級別的框架。ACE是跨平臺的,完全可以在Windows上開發(fā),運行在Linux上(當然,與Java不完全相同的是您需要重新編譯鏈接。而且如果您的程序使用了GUI的包,這么做是行不通的)。ACE的簡介就到這里。當我們使用ACE開發(fā)了一些程序,對ACE有了一定的認識之后,再回過頭來進一步認識ACE。本指南的主要內容本指南的目標是ACE零基礎到可以用ACE開發(fā)一些簡單的C/S程序。因此本指南的主要內容有:1.ACE的獲取編譯 首先介紹如何搭建ACE開發(fā)環(huán)境的問題。2.ACEReactor/Proactor框架 接下來本文并不打算像其它ACE的書籍從ACE歷史介紹,基礎機制講起,而是直接進入Reactor/Proactor框架的介紹及使用這2個框架來開發(fā)程序,給初學者最想要的東西。3.ACE其他機制 最后對ACE的其他框架機制等做一概要介紹,比如ACE_Task機制,線程管理,同步機制等。獲取ACE從服務器上獲取ACE-5.5.zip?;蛘邚?previous_versions/下載你需要的ACE版本。編譯ACE為什么要編譯ACE應用程序在鏈接及運行時需要使用ACE的庫和dll文件(Windows),而你下載的包里沒有這些文件或者不適合你的平臺。這時候就需要自己編譯ACE。編譯一般來說需要花很長時間。注1:項目組的其他人或者公司里的同事可能已經有編譯好的庫文件,如果你不想花時間編譯ACE,可以向其他人索取。服務器上有ace5.3版本在gcc2.96/3.23編譯通過的rpm文件,如果適合你的平臺,直接安裝即可。注2:下面的在Windows/Linux編譯部分取自陳昕發(fā)的貼,本文做了適當修改。文中目錄等請注意修改成符合你的環(huán)境的目錄等。陳昕原貼在8/bbs/viewthread.php?tid=1085&fpage=1在Window上編譯1.解壓縮到本地目錄例:解壓縮到C:\ACE_wrappers2.查看安裝向導ACE有自帶的安裝向導文件C:\ACE_wrappers\ACE-INSTALL.html查看ACE-INSTALL.html文件中的BuildingandInstallingACEonWindowswithMicrosoftVisualC++章節(jié),按照上面所寫即可,下面是對此過程的總結3.確定編譯環(huán)境本文使用的是C++.netframework1.1,以下為該編譯環(huán)境下的步驟4.新建config.h文件由于ACE支持很多操作系統(tǒng),因此必須建立一個config.h文件,在該文件包含相應的操作系統(tǒng)的頭文件,從而讓ACE知道將在什么操作系統(tǒng)下進行編譯進入C:\ACE_wrappers\ace目錄,新建config.h文件,在config.h文件中添加#include"ace/config-win32.h"如果系統(tǒng)為Windows98/Me,那么需要添加#defineACE_HAS_WINNT40如果需要使用標準C++的類庫(例iostream等),那么需要添加#defineACE_HAS_STANDARD_CPP_LIBRARY1如果需要使用MFC(不推薦),那么需要添加#defineACE_HAS_MFC1
5.設置環(huán)境變量開始->運行->cmdsetACE_ROOT=C:\ACE_wrappers6.使用.net環(huán)境編譯打開ACE.sln,可進行debug/release編譯7.生成.lib.dll文件生成的lib和dll文件在C:\ACE_wrappers\lib。在Linux上編譯注:該步驟使用ACE-5.4.7測試成功1.解壓縮到本地目錄例:#pwd/home/xchen#tar-xzvfACE.tar.gz2.設置環(huán)境變量#su–#vi/etc/profile在exportPATHUSERLOGNAMEMAILHOSTNAMEHISTSIZEINPUTRC下加入以下兩行exportACE_ROOT=/usr/local/ACE_wrappersexportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ACE_ROOT/ace#./etc/profile3.建立編譯環(huán)境#cd/home/xchen/ACE_wrappers#mkdirbuild#cdbuild#../configure4.配置config.h文件../configure之后,系統(tǒng)會在/home/xchen/ACE_wrappers/build下建立ace/conig.h文件在//ACEconfigurationheaderfile下行添加#include"ace/config-linux.h"5.編譯#cd/home/xchen/ACE_wrappers/build#make6.生成.so文件生成的so文件在/home/xchen/ACE_wrappers/build/ace/.libs目錄下。前行的路標對ACE感興趣,可以訪問ACE網站:/%7Eschmidt/ACE.htmlACE的YahooGroup:/group/ace-users/ACE開發(fā)者論壇(中文):/Index.htmlACEReactor框架Reactor(反應器)框架在編寫通訊程序時,如果服務器需要處理多個客戶端的連接,傳統(tǒng)做法是為每個客戶端創(chuàng)建一個進程或者線程。在大多數情況下,這種方法是可以解決問題的。但是,在某些情況下,進程線程創(chuàng)建維護并發(fā)的開銷是無法讓人接受的。而ACEReactor框架通過事件多路分離器,只需要用一個進程或者線程就可以處理多個客戶端的連接的事件。而且用戶程序使用Reactor框架也非常的簡單,只需要做3件事情:1)從ACE_Event_Handler派生子類,并Overwrite特定的事件虛回調方法。(比如handle_input,handle_timeout)。2)向ACE_Reactor類登記你剛才創(chuàng)建的ACE_Event_Handler子類。3)運行ACE_Reactor事件循環(huán)。Reactor的含義—被動反應,可以讓我們理解它的精髓:它是事件驅動的,就像Windows的事件驅動一樣。下面是ACEReactor框架的類圖和描述。ACE類描述ACE_Time_Value提供時間和持續(xù)時間(Duration)的可移植,規(guī)范化的表示ACE_Event_Handler抽象類,其定義的掛鉤(hook)方法是ACE_Reactor回調的目標。大多數通過ACE開發(fā)的應用事件處理器都是ACE_Event_Handler的后代。ACE_Timer_Queue抽象類,定義定時器隊列的能力和接口。ACE含有多種派生自ACE_Timer_Queue的類,為不同的定時機制提供了靈活的支持。ACE_Reactor提供了一個接口,用來在ACE框架中管理事件處理器登記,并執(zhí)行事件循環(huán)來驅動事件檢測,多路分離和分派。我們這里只關心ACE_Event_Handler和ACE_Reactor類。ACE_Event_Handler(事件處理器)ACE_Event_Handler是ACE中所有反應式事件處理器的基類。其接口如下所示:方法描述ACE_Event_Handler()指派與事件處理器相關聯(lián)的ACE_Reactor指針。~ACE_Event_Handler()將其自身從反應器的通知機制中移出。handle_input()輸入事件(如連接或數據事件)發(fā)生時的掛鉤(hook)方法。handle_output()輸出事件成為可能時(如流控制緩和或非阻塞式連接完成時)被調用的掛鉤方法。handle_exception()異常事件(如TCP緊急數據的到達)發(fā)生時被調用的掛鉤方法。handle_timeout()在定時器到期時被調用的掛鉤方法。handle_signal()OS發(fā)出信號時(或是POSIX信號,或是Windows同步對象遷移到激發(fā)(Signaled)狀態(tài)時)被調用的掛鉤方法。handle_close()在其他handle_*()掛鉤方法返回-1或者當ACE_Reactor::remove_handler()被顯式調用來解除事件處理器的登記時,執(zhí)行用戶定義的中止活動的掛鉤方法。get_handle()返回底層的I/O句柄。如果事件處理器只處理時間驅動的時間,該方法可實現為no-op(空操作)reactor()訪問器,用于獲取/設置可與ACE_Event_Handler相關聯(lián)的ACE_Reactor指針。priority()訪問器,用于獲取/設置事件處理器的優(yōu)先級。在開發(fā)一個普通的C/S應用程序時,只需Overwritehandle_input()和handle_close()這2個回調方法也就足夠了??梢栽赗eactor的定時器隊列設置定時器(參考下面的Reactor接口),當超時事件發(fā)生時handle_timeout()會來處理超時時應執(zhí)行的操作。下面討論一下使用ACE_Event_Handler時注意的3個方面:1.事件類型:在向Reactor登記事件處理器的時候,必須指定事件類型。事件類型定義在ACE_Event_Handler中,包括下面幾種:事件類型描述READ_MASK指定輸入事件(如數據出現在socket或文件句柄上)。反應器分派handle_input()來處理輸入事件。WRITE_MASK指定輸出事件(如在流控制緩和時)。反應器分派handle_output()來處理輸出事件。EXCEPT_MASK指定異常事件(如緊急數據出現在socket上)。反應器分派handle_exception()來處理異常事件。ACCEPT_MASK指定被動模式的連接事件。反應器分派handle_input()來處理連接事件。CONNECT_MASK指定非阻塞式連接的完成。反應器分派handle_output()來處理非阻塞式連接的完成事件。可以對這些事件進行組合(|)來高效的定義一組事件。這組事件被填充在ACE_Reactor::register_handler()方法的ACE_Reactor_Mask參數。2.掛鉤方法的返回值登記的事件發(fā)生時,反應器會分派相應的handle_*()來處理。而這些方法的返回值需注意:返回值描述0指示反應器應繼續(xù)為此事件處理器檢測和分派所登記的事件。對于需要重復處理一個事件的事件處理器(比如當有數據可讀時從Socket讀取數據),這種行為是常見的。>0也指示反應器應繼續(xù)為此事件處理器檢測和分派所登記的事件。此外,如果在處理了某個I/O事件后返回值大于0,反應器將在阻塞它的事件多路分離器之前,再次在句柄上分派此事件處理器。-1指示反應器停止為此事件處理器檢測已登記的事件。在反應器移除此事件處理器之前,它調用事件處理器的handle_close()方法,傳給該方法正在被解除登記的事件的ACE_Reactor_Mask值。所以,我們在使用Reactor框架來開發(fā)C/S程序時,一般應在handle_*()方法里返回0。3.清理事件處理器handle_close()方法會在某個掛鉤方法決定要進行清理時被調用。handle_close()方法可隨即進行用戶定義的關閉活動,如釋放內存,關閉IPC對象等。Reactor框架會忽略handle_close()方法的返回值。如上所述,Reactor只會在掛鉤方法返回負值,或事件處理器被顯式移除時調用handle_close(),而不會在IPC機制到達文件末尾或I/O句柄被本地或遠程關閉時調用handle_close()。因此,應用必須檢測I/O句柄何時關閉,并采取措施。比如,當recv()或read()調用返回0時,事件處理器應從handle_*()方法里返回-1,或調用ACE_Reactor::remove_handler()。ACE_ReactorACE_Reactor是Reactor模式的核心類,Reactor模式的注冊事件處理器,運行事件循環(huán)等功能都要通過這個類來進行。ACE_Reactor實現了Fa?ade模式,為應用程序提供了各種訪問Reactor框架特性的接口。1.初始化和析構方法:方法描述ACE_Reactor()open()這兩個方法創(chuàng)建并初始化反應器的實例。~ACE_Reactor()close()這兩個方法清理反應器在初始化時分配的資源。2.事件處理器管理方法方法描述register_handler()為基于I/O和信號的事件登記事件處理器remove_handler()移除某事件處理器,使其不再參加基于I/O和信號的事件分派suspend_handler()暫時停止分派事件給某事件處理器resume_handler()恢復為先前掛起的事件處理器分配事件mask_ops()獲取,設置,增加或者清除與某事件處理器相關的事件類型及掩碼schedule_wakeup()將指定的掩碼增加到某事件管理器的條目中,該處理器在之前必須已通過register_handler()登記過。cancel_wakeup()從某事件處理器的條目中清除指定的掩碼,但并不移除該事件處理器3.事件循環(huán)(Event-loop)方法方法描述handle_events()等待事件發(fā)生,并隨即分派與之相關聯(lián)的事件處理器??赏ㄟ^超時參數限制花費在等待事件上的時間。run_reactor_event_loop()反復調用handle_events()方法,直至其失敗,或者reactor_event_loop_done()返回1,或者超時(可選)。end_reactor_event_loop()指示反應器關閉其事件循環(huán)。reactor_event_loop_done()在反應器的事件循環(huán)被end_reactor_event_loop()調用結束時返回14.定時器管理方法方法描述schedule_timer()登記一個事件處理器,它將在用戶規(guī)定的時間后被執(zhí)行。cancel_timer()取消一個或多個之前登記的定時器。我們可以使用這兩個方法來使代碼具有超時特性,當超時發(fā)生時,事件處理器的handle_timeout()方法會被調用。5.通知方法反應器擁有一種通知機制,應用可將其用于把事件和事件處理器插入反應器的分派引擎中。方法描述notify()將事件(及可選的事件處理器)插入反應器的事件檢測器中,從而使其在反應器下次等待事件時被處理max_notify_iterations()設置反應器將在其通知機制中分派的處理器的最大數目。purge_pending_notifications()從反應器的通知機制中清除指定的事件處理器或所有事件處理器6.實用方法(UtilityMethod)方法描述instance()靜態(tài)方法,返回指向單體(Singleton)ACE_Reactor的指針。這是通過Singleton模式和Double-CheckedLockingOptimization模式來創(chuàng)建和管理的。owner()使某個線程“擁有”反應器的事件循環(huán)。因為大部分的應用程序都需要實現Server或者Client端的內容,所以下面我們將分別實現一個Reactor的Server和Client。ACE也提供了實現Server和Client的半成品,Acceptor和Connector框架。Acceptor(接受器)-Connector(連接器)框架下面是Acceptor-Connector框架類的類圖。圖中我們可以看到ACE_Acceptor,ACE_Connector繼承自我們前面提到的ACE_Event_Handler。然后ACE_Svc_Handler是繼承自ACE_Task的,而ACE_Task其實也是ACE_Event_Handler的后代。根據前面提到的Reactor開發(fā)3步驟,我們只需要將ACE_Svc_Handler登記到Reactor即可進行事件處理。對我們開發(fā)應用程序而言,我們只需要關心ACE_Acceptor,ACE_Connector,ACE_Svc_Handler這3個類。ACE類描述ACE_Svc_Handler表示某個已連接服務的本地端,其中含有一個用于與相連對端通信的IPC端點。ACE_Acceptor該工廠被動的等待接受連接,并隨即初始化一個ACE_Svc_Handler來響應來自對端的主動連接請求。ACE_Connector該工廠主動的連接到對端接受器,并隨即初始化一個ACE_Svc_Handler與其相連對端通信。通俗的說,ACE_Acceptor就是我們要實現的Server,ACE_Connector就是我們要實現的Client。當Client連接到Server時,會創(chuàng)建一個ACE_Svc_Handler來處理事件(比如接收到數據,連接被斷開等);而Server端也會為每個Client創(chuàng)建一個ACE_Svc_Handler來處理事件。ACE_Svc_Handler(服務處理器)如上所述,ACE_Svc_Handler就是用來處理事件,那么主要需要處理那些事件呢?1.服務創(chuàng)建和激活方法方法描述ACE_Svc_Handler()由接受器或連接器在創(chuàng)建服務處理器時調用的構造方法open()由接受器或連接器自動調用來初始化服務處理器的掛鉤方法。ACE_Svc_Handler已經在open方法里,把自己登記到Reactor事件循環(huán),登記的事件類型為READ_MASK。所以我們的代碼將變得更加簡單。2.服務處理方法方法描述svc()ACE_Svc_Handler從ACE_Task繼承來的svc()掛鉤方法。在服務處理器的activate()方法被調用后,其大部分后續(xù)處理都可在svc()掛鉤方法中執(zhí)行。handle_*()ACE_Svc_Handler從ACE_Event_Handler繼承的handle_*()方法。因此,服務處理器可以向反應器登記自身,以在它感興趣的各種事件發(fā)生時接收像handle_input()這樣的回調。peer()返回指向底層PEER_STREAM的引用。服務處理器的PEER_STREAM是在其open()掛鉤方法被調用時就緒的。任何處理方法都可使用這個訪問器來獲取指向已連接的IPC機制的引用。3.服務關閉方法方法描述destroy()可以用來直接關閉服務處理器handle_close()通過來自反應器的回調調用destroy()close()從服務處理器退出時調用handle_close()應用可以直接調用destroy()來關閉服務處理器。該方法執(zhí)行了以下步驟:1)從反應器處移除處理器2)取消任何與此處理器相關的定時器3)關閉對端流對象,以免發(fā)生句柄泄漏4)如果對象是被動態(tài)分配的,將其刪除,以免內存泄漏。下面討論一下如何在代碼中使用ACE_Svc_Handler。1.ACE_Svc_Handler是一個類模板,定義它的子類時,要指定模板的類型。PEER_STREAM:底層傳遞數據流的類型,Socket連接使用ACE_SOCK_StreamSYNCH_STRATEGY:同步策略,可以使用ACE_NULL_SYNCH(無同步),ACE_MT_SYNCH(ACE默認提供的同步策略)。2.子類覆蓋事件類型相應的handle_*()方法。ACE_AcceptorACE_Acceptor是一個工廠,它實現了Acceptor-Connector中的Acceptor角色。也就是說起到了Server的作用。首先我們看一下ACE_Acceptor提供了那些接口:1.初始化,析構及訪問器方法方法描述ACE_Acceptor()open()將接受器的被動IPC端點綁定到特定地址,比如TCP端口或者IPC主機地址,然后對連接請求的到達進行監(jiān)聽~ACE_Acceptor()close()關閉接受器的IPC端點,并釋放資源。acceptor()返回指向底層PEER_ACCEPTOR的引用。2.連接建立和服務處理器初始化方法方法描述handle_input()當連接請求從對端連接器到達時,反應器會調用此模板方法。它可以使用在下面概述的3個方法來使用“被動地連接IPC端點并創(chuàng)建和激活與其相關聯(lián)的服務處理器”所必需的各步驟自動化。make_svc_handler()該工廠方法創(chuàng)建服務處理器來處理通過其已連接的IPC端點,從對端服務發(fā)出的數據請求accept_svc_handler()該掛鉤方法使用接受器的被動模式IPC端點來創(chuàng)建已連接的IPC端點,并將此端點與和服務處理器相關聯(lián)的一個I/O句柄封裝在一起activate_svc_handler()該掛鉤方法調用服務處理器的open(),使服務處理器完成對自己的初始化。下面的順序圖詳細的描述了當連接請求到達時,ACE_Acceptor所做的工作:那么我們在應用程序中如何使用ACE_Acceptor呢?1.首先,實現ACE_Svc_Handler子類,然后覆蓋相應的handle_*()事件回調方法。2.沒有特殊需要的情況下,我們不需要創(chuàng)建ACE_Acceptor的子類,只須定義它的實例即可。ACE_Acceptor是一個類模板,定義它的實例時,要指定模板的類型。SVC_Handler:指定Acceptor對應的ACE_Svc_Handler,當有連接請求時,Acceptor會創(chuàng)建一個新的ACE_Svc_Handler來處理對端的事件。PEER_ACCEPTOR:它能夠被動的接受客戶連接,常被指定為ACE的各個IPCWrapperFa?ade。對于Socket來說,可以指定為ACE_SOCK_Acceptor。3.使用Open()方法,即可啟動服務器,開始監(jiān)聽客戶機事件。對于Socket來說,Open方法需要使用一個ACE_INET_Addr。ACE_Connector同ACE_Acceptor相同,ACE_Connector也是一個工廠,它實現了Acceptor-Connector中的Connector角色。也就是說起到了Client的作用。ACE_Connector的接口有:1.連接器初始化,析構方法以及訪問器方法方法描述ACE_Connector()open()用于初始化連接器的方法~ACE_Connector()close()釋放連接器所用的資源connector()返回指向底層的PEER_CONNECTOR的引用2.連接建立和服務處理器初始化方法:可用于主動地建立連接,并對服務處理器進行初始化。方法描述connect()應用會在想要將某個服務處理器連接到監(jiān)聽的對端時,調用這個模板方法。它可以使用下面的3個方法來使“主動連接某個IPC端點,創(chuàng)建并激活與其相關聯(lián)的服務處理器”所必需的各步驟自動化。make_svc_handler()該工廠方法提供服務處理器,后者會使用已連接的IPC端點connect_svc_handler()該掛鉤方法使用服務處理器的IPC端點來同步或異步地主動連接端點activate_svc_handler()該掛鉤方法調用服務處理器的open()掛鉤方法,后者允許服務處理器在連接建立之后完成對其自身的初始化。handle_output()在異步發(fā)起的連接請求完成之后,反應器會調用這個模板方法。它調用activate_svc_handler()方法,以讓服務處理器對其自身進行初始化。cancel()取消某服務處理器,其連接之前是異步發(fā)起的。調用者—不是連接器—負責關閉服務處理器。與Acceptor有些不同的是,Connector在連接時可以選擇同步或異步連接。因為對于Acceptor來說,建立連接通常是即時的,而主動連接建立就要花較長的一段時間,尤其是廣域網。我們這里只討論同步連接的情形。下面的順序圖詳細的描述了異步連接建立中的各步驟:接下來討論一下在應用程序中如何使用ACE_Connector呢?(同ACE_Acceptor基本類似)1.首先,實現ACE_Svc_Handler子類,然后覆蓋相應的handle_*()事件回調方法。2.沒有特殊需要的情況下,我們也不需要創(chuàng)建ACE_Connector的子類,只須定義它的實例即可。ACE_Connector是一個類模板,定義它的實例時,要指定模板的類型。SVC_Handler:指定Connector對應的ACE_Svc_Handler,同服務器建立連接時,Connector會創(chuàng)建一個新的ACE_Svc_Handler來處理對端的事件。PEER_Connector:它能夠主動的建立客戶連接,常被指定為ACE的各個IPCWrapperFa?ade。對于Socket來說,可以指定為ACE_SOCK_Connector。3.使用connect()方法去主動連接。connect方法里的參數有你剛才創(chuàng)建的ACE_Svc_Handler子類的指針和待連接的PEER_Address(對于Socket來說,是ACE_INET_Addr)。ACEReactorServer(Demo)需求下面我們實現一個簡單的Server端,這個Server能夠處理多個Client的連接,在收到Client的消息后,原封不動的將消息再發(fā)還給Client。實現接下來我們一步一步實現它(在RedHat9,使用MagicC++開發(fā),最終的代碼可以在前面指定位置獲得):1.使用MagicC++在Linux服務器上建立一個新工程ReactorServer2.如果系統(tǒng)不能從環(huán)境變量等找到include的ACE頭文件,那么你需要在項目設定的Compile項中指定。比如/home/work/ACE_wrappers3.在項目設定的Link的選項卡中,庫文件處加入ACE庫的支持。同上,如果系統(tǒng)不能從環(huán)境變量等找到ACE庫文件,你也需要在下面的Librarydirectories中指定。4.準備工作做好之后,下面我們創(chuàng)建服務處理器。創(chuàng)建新類ServerHandler,繼承自ACE_Svc_Handler。修改頭文件ServiceHandler.h。1)由于我們創(chuàng)建的是Socket服務器,ACE_Svc_Handler的模板里PEER_STREAM被指定為ACE_SOCK_STREAM,同時我們不需要使用同步機制,模板里的SYNCH_STRATEGY被指定為ACE_NULL_SYNCH。classServerHandler:publicACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH>2)我們只關心輸入事件,也就是客戶端發(fā)來的消息,覆蓋handle_input方法。 virtualinthandle_input(ACE_HANDLEfd=ACE_INVALID_HANDLE);下面是ServiceHandler.h的完整代碼#ifndefSERVERHANDLER_H#defineSERVERHANDLER_H #include"ace/Svc_Handler.h"#include"ace/SOCK_Stream.h" classServerHandler:publicACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH>{ public: virtualinthandle_input(ACE_HANDLEfd=ACE_INVALID_HANDLE);};#endif5.下面我們來實現ServiceHandler的handle_input方法。修改cpp文件ServiceHandler.cpp。1)在handle_input方法里,我們定義了一個4096的char數組。并出始化該數組。 constintINPUT_SIZE=4096; charbuffer[INPUT_SIZE]; memset(buffer,0,INPUT_SIZE);2)然后使用底層的peer來接收收到的消息,recv返回了收到的消息的length。 intrecv_cnt=this->peer().recv(buffer,sizeof(buffer));3)如果length<=0,說明對端的連接被斷開,這時候應返回-1,指示反應器停止為此事件處理器檢測已登記的事件。 if(recv_cnt<=0) { printf("Connectionclose\n"); return-1; }4)然后我們再使用底層的peer將收到的消息原封不動的發(fā)回去。 this->peer().send(buffer,recv_cnt); 5)handle_input結束時,我們應該返回0,當下次有消息到來時,仍能被此服務處理器處理。 return0;下面是ServiceHandler.cpp的完整代碼#include"ServerHandler.h" intServerHandler::handle_input(ACE_HANDLE){ constintINPUT_SIZE=4096; charbuffer[INPUT_SIZE]; memset(buffer,0,INPUT_SIZE); intrecv_cnt=this->peer().recv(buffer,sizeof(buffer)); if(recv_cnt<=0) { printf("Connectionclose\n"); return-1; } this->peer().send(buffer,recv_cnt); return0;}6.最后我們實現main方法,增加ReactorServer.cpp文件1)在main方法,首先定義監(jiān)聽的IP地址,對于我們的Server應用來說,只須指定監(jiān)聽的端口即可。我們在9000端口監(jiān)聽。(ACE_INET_Addr后面介紹) ACE_INET_Addrport_to_accept(9000);2)定義我們的Server,Acceptor模板類的SVC_Handler指定為我們剛剛實現的ServerHandler,PEER_ACCEPTOR指定為ACE_SOCK_Acceptor。然后使用open方法開始監(jiān)聽。 ACE_Acceptor<ServerHandler,ACE_SOCK_ACCEPTOR>server; if(server.open(port_to_accept)==-1) { return-1; }3)接下來運行ACE_Reactor事件循環(huán)。 ACE_Reactor::instance()->run_reactor_event_loop();下面是ReactorServer.cpp的完整代碼#include"ace/Reactor.h"#include"ace/Acceptor.h"#include"ace/SOCK_Acceptor.h" #include"ServerHandler.h"intmain(intargc,char*argv[]){ ACE_INET_Addrport_to_accept(9000); ACE_Acceptor<ServerHandler,ACE_SOCK_ACCEPTOR>server; if(server.open(port_to_accept)==-1) { return-1; } ACE_Reactor::instance()->run_reactor_event_loop(); return0;}7.接下來編譯,運行ReactorServer8.下面我們使用一個客戶端來測試一下。這里使用的是SocketTest,一個Java寫的Socket測試工具,你可以在前面指定的位置上找到它。打開SocketTest,輸入我們的Server所在的IP地址和端口后,點擊Connect。連接之后,在下面的Message框里輸入一句話,然后點Send按鈕,我們就會看到Conversationwithhost框里看到我們發(fā)過去的消息,以及服務器原封不動回復的消息。9.上面的測試說明我們的服務器是按照我們的預想工作的,下面我們再測試一下同時有幾個客戶端連接服務器的情況。我們再打開2個SocketTest,重復前面的操作。10.多個客戶端同時連上去也沒有關系,我們的Server同樣能很好的運行。這個Server實現了以下功能:在指定端口監(jiān)聽處理多個客戶端的請求將接受到的消息回復給客戶端這個Server是跨平臺的,只需在不同平臺分別編譯運行。而我們完成這些功能只用了:一個線程幾十行代碼幾分鐘的時間由此可見,使用ACE的好處是我們無需再關心Socket等連接的細節(jié),也無需關心為每個客戶端創(chuàng)建線程,我們只需專注業(yè)務邏輯,這極大的提高了我們的效率。ACE工具類前面我們在編寫Server時用到了ACE_INET_Addr,其實ACE提供了很多的工具類(宏)。下面對這些工具類做一簡單的介紹,更詳細的內容請自行閱讀參考資料。作用類(宏)描述地址包裝類ACE_INET_AddrACE_INET_Addr類封裝了Internet域地址族(AF_INET)。該類派生自ACE中的ACE_Addr。它的多種構造器可用于初始化對象,使其具有特定的IP地址和端口。除此而外,該類還具有若干set和get方法,并且重載了比較操作,也就是==操作符和!=操作符。ACE_UNIX_AddrACE_UNIX_Addr類封裝了UNIX域地址族(AF_UNIX),它也派生自ACE_Addr。該類的功能與ACE_INET_Addr類相似。時間包裝類ACE_TIME_VALUEACE的時間包裝類,該類使用秒(sec)和微秒(usec)來描述時間。日志宏ACE_DEBUG/ACE_TRACEACE_DEBUG(與ACE_TRACE幾乎相同)和ACE_ERROR宏用于打印調試及錯誤信息及記錄相應的日志。它們的用法是:ACE_DEBUG((severtity,formating-args))ACE_ERROR((severtity,formating-args))severtity為嚴重級別,最常用的是LM_DEBUG,LM_ERROR。formating-args使用了ACE_TEXT宏,ACE_TEXT可以使用%d,%t等格式修飾符。就如在printf格式串中一樣,下面簡單介紹幾個格式修飾符(區(qū)分大小寫)。%P當前進程的ID%t調用線程的ID%T當前時間,格式為hour:min:sec.usec%s字符串ACE_ERROR內存分配宏ACE_NEW(p,c)使用構造器c分配內存,并把指針賦給p。失敗時,p被設置為0,并執(zhí)行returnACE_NEW_RETURN(p,c,r)使用構造器c分配內存,并把指針賦給p。失敗時,p被設置為0,并執(zhí)行returnrACE_NEW_RETURN(p,c)使用構造器c分配內存,并把指針賦給p。失敗時,p被設置為0,并繼續(xù)執(zhí)行下一條語句。Server改進下面對我們剛才的Server進行改進,讓它打印出一些調試信息來。1)Client連接時,打印出Client端的IP地址;2)收到Client的消息時,打印出消息的內容;3)Client斷開時,打印出Client斷開的信息。1.打開ServiceHandler.h進行修改:1)為方便起見,我們創(chuàng)建了super類型定義。這樣,在我們的類中調用基類方法時,可讀性將大為提高。 typedefACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH>super;2)當Client連接時,打印出Client端的IP地址,應該在open()方法里面實現,所以我們繼承父類的open方法: intopen(void*=0);3)將Client端的IP地址保存在類里,因此增加一個屬性: private: ACE_TCHARpeer_name[MAXHOSTNAMELEN];ServiceHandler.h修改完成,下面是改進后的ServiceHandler.h的完整代碼#ifndefSERVERHANDLER_H#defineSERVERHANDLER_H #include"ace/Svc_Handler.h"#include"ace/SOCK_Stream.h" classServerHandler:publicACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH>{ typedefACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH>super; public: intopen(void*=0); virtualinthandle_input(ACE_HANDLEfd=ACE_INVALID_HANDLE); private: ACE_TCHARpeer_name[MAXHOSTNAMELEN];};#endif2.修改類文件ServiceHandler.cpp。1)增加open方法,我們并不打算改變父類open的行為,所以首先調用父類的open方法。intServerHandler::open(void*p){ if(super::open(p)==-1) { return-1; } return0;}2)接下來在open方法里獲得Client的IP地址。使用底層peer的get_remote_addr方法。得到地址之后,我們ACE_DEBUG用打印出來,同時打印出時間,進程號,線程號。 ACE_INET_Addrpeer_addr; if(this->peer().get_remote_addr(peer_addr)==0 &&peer_addr.addr_to_string(peer_name,MAXHOSTNAMELEN)==0) { ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Connectionfrom%s\n"),peer_name)); }3)修改handle_input方法,把原來用printf寫的方法改成ACE_DEBUG。再把收到的消息打印出來。 if(recv_cnt<=0){ ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Connectionclosefrom%s\n"),peer_name)); return-1; } ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Receivemsgfrom%s:%s\n"),peer_name,buffer));下面是改進后的ServiceHandler.cpp的完整代碼#include"ServerHandler.h"intServerHandler::open(void*p){ if(super::open(p)==-1) { return-1; } ACE_INET_Addrpeer_addr; if(this->peer().get_remote_addr(peer_addr)==0 &&peer_addr.addr_to_string(peer_name,MAXHOSTNAMELEN)==0) { ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Connectionfrom%s\n"),peer_name)); } return0;}intServerHandler::handle_input(ACE_HANDLE){ constintINPUT_SIZE=4096; charbuffer[INPUT_SIZE]; memset(buffer,0,INPUT_SIZE); intrecv_cnt=this->peer().recv(buffer,sizeof(buffer)); if(recv_cnt<=0){ ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Connectionclosefrom%s\n"),peer_name)); return-1; } ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Receivemsgfrom%s:%s\n"),peer_name,buffer)); this->peer().send(buffer,recv_cnt); return0;}3.這樣修改就完成了,接下來編譯測試,運行ReactorServer,然后運行SocketTest測試,效果類似下圖:ACEReactorClient(Demo)需求上面ReactorServer的測試的客戶端,我們使用了SocketTest這個工具。下面我們自己開發(fā)一個工具。這個工具能夠:1)建立5個與服務器端的連接;2)每個連接每隔2秒鐘發(fā)送一條測試消息給服務器,發(fā)送5次,然后斷開同服務器的鏈接。實現I接下來我們一步一步實現它(在RedHat9,使用MagicC++開發(fā),最終的代碼可以在前面指定位置獲得):1.使用MagicC++在Linux服務器上建立一個新工程ReactorClient。項目的設置等跟ReactorServer的設置相同2.準備工作做好之后,下面我們創(chuàng)建服務處理器。創(chuàng)建新類ClientHandler,繼承自ACE_Svc_Handler。修改頭文件ClientHandler.h。1)由于我們創(chuàng)建的是Socket服務器,ACE_Svc_Handler的模板里PEER_STREAM被指定為ACE_SOCK_STREAM,同時我們不需要使用同步機制,模板里的SYNCH_STRATEGY被指定為ACE_NULL_SYNCH。classClientHandler:publicACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH>2)覆蓋handle_input方法,以檢查服務器是否將發(fā)出去的消息返回來。 virtualinthandle_input(ACE_HANDLEfd=ACE_INVALID_HANDLE);下面是ClientHandler.h的完整代碼#ifndefCLIENTHANDLER_H#defineCLIENTHANDLER_H#include"ace/Svc_Handler.h"#include"ace/SOCK_Stream.h" classClientHandler:publicACE_Svc_Handler<ACE_SOCK_STREAM,ACE_NULL_SYNCH>{public: virtualinthandle_input(ACE_HANDLEfd=ACE_INVALID_HANDLE);};#endif3.下面我們來實現ClientHandler的handle_input方法。這個實現跟Server的幾乎一樣,但只負責接收消息,不再發(fā)送消息給服務器端。下面是ClientHandler.cpp的完整代碼#include"ClientHandler.h"intClientHandler::handle_input(ACE_HANDLE){ constintINPUT_SIZE=4096; charbuffer[INPUT_SIZE]; memset(buffer,0,INPUT_SIZE); intrecv_cnt=this->peer().recv(buffer,sizeof(buffer)); if(recv_cnt<=0){ ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Connectionclose\n"))); return-1; } ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Receivemsgfromserver:%s\n"),buffer)); return0;}4.最后我們實現main方法,增加ReactorClient.cpp文件1)在main方法,首先定義要連接的IP地址,對于我們的Client應用來說,要連接的服務器為本機9000端口上的服務器??梢园凑障旅娴姆椒▉矶xACE_INET_Addr。 ACE_INET_Addrport_to_connect(9000,ACE_LOCALHOST);2)定義我們的Client,Connector模板類的SVC_Handler指定為我們剛剛實現的ClientHandler,PEER_ACCEPTOR指定為ACE_SOCK_Acceptor。 ACE_Connector<ClientHandler,ACE_SOCK_Connector>client;3)根據我們的需求,創(chuàng)建5個到服務器的連接,connect方法和Acceptor的open不同,open方法不需要制定SVC_Handler的指針,而connect方法需要。不過你可以傳個空指針進去,connect方法會自動創(chuàng)建一個新的SVC_Handler,并且由于connect方法的參數是SVC_Handler指針的引用,所以我們能夠得到connect方法里面創(chuàng)建的SVC_Handler的實際指針。 for(inti=0;i<5;i++) { ClientHandler*handler=NULL; if(client.connect(handler,port_to_connect)==-1) { ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Connection%dfailed.\n"),i)); return-1; } }4)接下來運行ACE_Reactor事件循環(huán)。 ACE_Reactor::instance()->run_reactor_event_loop();下面是ReactorClient.cpp的完整代碼#include"ace/Reactor.h"#include"ace/Connector.h"#include"ace/SOCK_Connector.h" #include"ClientHandler.h"intmain(intargc,char*argv[]){ ACE_INET_Addrport_to_connect(9000,ACE_LOCALHOST); ACE_Connector<ClientHandler,ACE_SOCK_Connector>client; for(inti=0;i<5;i++) { ClientHandler*handler=NULL; if(client.connect(handler,port_to_connect)==-1) { ACE_DEBUG((LM_DEBUG,ACE_TEXT("[%T](%P|%t)Connection%dfailed.\n"),i)); return-1; } } ACE_Reactor::instance()->run_reactor_event_loop(); return0;}5.接下來編譯。如果沒有運行ReactorServer,請首先運行ReactorServer。然后再運行ReactorClient。運行效果如下圖:根據運行結果,我們發(fā)現,我們的測試程序成功的與服務器建立了5個連接,但是由于我們還沒有編寫發(fā)送消息的代碼,所以還不能每2秒發(fā)送消息。使用超時機制發(fā)送消息從ReactorServer.cpp的main方法我們看到,當5個連接成功后,就進入到了Reactor的事件循環(huán)中了,而沒有事件觸發(fā),是不會調用我們的服務處理器的。因此,我們的主動發(fā)送消息的代碼也就沒有機會執(zhí)行。解決這個問題,創(chuàng)造我們主動發(fā)送消息的機會,有2個辦法:1)外
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度麻醉師醫(yī)療事故處理與賠償合同4篇
- 二零二五年度環(huán)保項目竣工驗收質量監(jiān)管合同2篇
- 藥源基地合作協(xié)議
- 合金鑄球段項目融資渠道探索
- 20 青山不老 說課稿 -2024-2025學年語文六年級上冊統(tǒng)編版
- 二零二五年度集裝箱碼頭裝卸服務租賃合同3篇
- 2025版女方離婚上訴狀:離婚后財產分割與子女撫養(yǎng)費用分擔合同-@-1
- 2023七年級生物下冊 第四單元 生物圈中的人 第四章 人體內物質的運輸第三節(jié) 輸送血液的泵-心臟說課稿 (新版)新人教版
- 7《媽媽睡了》說課稿-2024-2025學年統(tǒng)編版語文二年級上冊
- 二零二五年度酒店式公寓租賃代理服務協(xié)議2篇
- 遼寧營口面向2024大學生退役士兵專考專招(95人)高頻考題難、易錯點模擬試題(共500題)附帶答案詳解
- 黑枸杞生物原液應用及產業(yè)化項目可行性研究報告
- 2024年河北廊坊市三河市金創(chuàng)產業(yè)投資有限公司招聘筆試參考題庫含答案解析
- TZSA 158-2023 雙引擎分布式視頻處理器技術規(guī)范
- 律師辦理刑事案件基本流程及風險防范課件
- TQGCML 2624-2023 母嬰級空氣凈化器 潔凈空氣和凈化等級技術要求
- 睡眠障礙護理查房課件
- 金融工程.鄭振龍(全套課件560P)
- 英語演講技巧和欣賞課件
- 六年級語文下冊閱讀及參考答案(12篇)
- 蘇教版(蘇少版)九年級美術下冊全冊課件
評論
0/150
提交評論