服务动态配置在编写服务端应用在有很明显的优点
编写ACE动态服务的步骤。
本文适用于ACE初学者。
1. 主进程
1.1 创建主程序
用Viusal Studio创建一空Win32 Console项目,这里命名为GLIVR86ServiceD.注,这里
D表示Daemon,不是Debug。表示我们以后会把这个项目改造为了NT_Service(以后介绍步骤.
1.2 修改项目属性 (Configuation Properties)
1.2.1 为项目新增主文件 GLIVR86ServiceD.cpp,目的是为项目属性中,增加C/C++选项
1.2.1 General 修改程序输出路径$(OutDir)
1.2.2.Debugging Command Arguments: -d, 以调试模式启动
1.2.3 C/C++设置
1.2.3.1 Additional Include Directories /I[path]: $(ACE_ROOT);
1.2.3.2 Code Generation: /MTd ;/MT 调试版选MTd,发行版选 MT
1.2.3.3 Preprocessor:Preprocesor Definitions/D: WIN32;_DEBUG;_CONSOLE;
这是调试版,发行版将_DEBUG改为NDEBUG
1.2.4 链接设置
1.2.4.1 Input: Additinal Dependencise: ACE(d).lib,调试版选aced.lib,
发行版选ace.lib
1.2.4.2 System: SubSystem /subsystem: Console ; (/SUBSYSTEM:CONSOLE)
主程序代码
// @description: IVR 86业务服务主程序入口
// @author: jiangtao
// @version:2.0.0
#include " stdafx.h "
#include < memory > // 使用 auto_ptr
#include " ACE/OS_NS_unistd.h "
#include " ACE/TP_Reactor.h "
#include " ACE/Reactor.h "
#include " ACE/Service_Config.h "
#include " ACE/Thread_Manager.h "
// 线程池
static ACE_THR_FUNC_RETURN event_loop ( void * arg)
{
ACE_DEBUG((LM_INFO, " (%P|%t),event_loop()/n " ));
ACE_Reactor * reactor = static_cast < ACE_Reactor *> (arg);
reactor -> owner (ACE_OS::thr_self ());
reactor -> run_reactor_event_loop ();
return 0 ;
}
int
ACE_TMAIN ( int argc, ACE_TCHAR * argv[])
{
const size_t N_THREADS = 4 ;
ACE_TP_Reactor tp_reactor;
ACE_Reactor reactor ( & tp_reactor);
auto_ptr < ACE_Reactor > delete_instance(ACE_Reactor::instance ( & reactor));
if (ACE_Service_Config::open (argc, argv) == - 1 )
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ( " %p/n " ),
ACE_TEXT ( " open " )),
1 );
ACE_Thread_Manager::instance () -> spawn_n
(N_THREADS, event_loop, ACE_Reactor::instance ());
ACE_Thread_Manager::instance () -> wait ();
return 0 ;
}
2. 创建被加载的服务的动态链接库
2.1 用Visual Studio创建一个新的项目GLIVR86Service,我们依然从空白的Win32 Console开
始
1.2.1 为项目新增主文件 GLIVR86Service.cpp,目的是为项目属性中,增加C/C++选项
1.2.1 General : Configration Type:改为 动态链接库 Dynamic Library(DLL)
1.2.3.1 Additional Include Directories /I[path]: $(ACE_ROOT);
1.2.3.2 Code Generation: /MTd ;/MT 调试版选MTd,发行版选 MT
1.2.3.3 Preprocessor:Preprocesor Definitions/D:
WIN32;_DEBUG;_WINDOWS;ACE_BUILD_SVC_DLL
这是调试版,发行版将_DEBUG改为NDEBUG
这里,特别注意,要增加 ACE_BUILD_SVC_DLL宏。如果用generate_export_file.pl
生成自定义的export头文件,这个宏也可以自定义
2.2.2 链接器设置
2.2.2.1 Input: Additinal Dependencise: ACE(d).lib,调试版选aced.lib,
发行版选ace.lib
2.2.2.2 System: SubSystem /subsystem: Console ; (/SUBSYSTEM:CONSOLE)
2.2.2.3 General ,Output file:
../GLIVR86ServiceD/GLIVR86ServiceD/GLIVR86ServiceD.dll
这里填写上GLIVR86ServiceD的路径或环境变量Path中指
示的路径,这样可以方便调试
2.2.2.4 Adanced, Import Libaray: $(OutDir)/GLIVR86ServiceD.lib
上面是调试版,发行版可以去掉后缀D,即
$(OutDir)/GLIVR86Service.lib
3.服务的动态链接库实现
3.1 为项目增加两个文件,分别声明和实现服务类工厂
//@file: ServiceFactory.h
//@file: ServiceFactory.cpp
代码分别如下
// @description: IVR 86业务服务
// @author: jiangtao
// @data: 2006-7-3
// @version:1.0.0
#ifndef SERVICEFACTORY_H
#define SERVICEFACTORY_H
#include " ACE/svc_export.h "
#include " ACE/Service_Config.h "
#include " ACE/Service_Object.h "
// 声明服务工厂
ACE_SVC_FACTORY_DECLARE (ServiceFactory_T)
class ACE_Svc_Export ServiceFactory_T : public ACE_Service_Object
{
public :
/// Initializes object when dynamic linking occurs.
virtual int init ( int argc, ACE_TCHAR * argv[]);
/// Terminates object when dynamic unlinking occurs.
virtual int fini ( void );
/// Returns information on a service object.
virtual int info (ACE_TCHAR ** info_string, size_t length = 0 ) const ;
};
#endif /* SERVICEFACTORY_H */
/*******************************************************************/
#include " ServiceFactory.h "
#include " ACE/Log_Msg.h "
// 实现服务工厂
ACE_SVC_FACTORY_DEFINE (ServiceFactory_T)
int ServiceFactory_T::init( int argc, ACE_TCHAR * argv[])
{
ACE_DEBUG((LM_INFO, " (%P|%t) 服务初始化完成/n " ));
return 0 ;
}
int ServiceFactory_T::info(ACE_TCHAR ** strp, size_t length) const
{
ACE_DEBUG((LM_INFO, " ServiceFactory_T::info() /n " ));
return 0 ;
}
int ServiceFactory_T::fini( void )
{
return 0 ;
}
5.服务配置文件svc.conf
dynamic IVR86Service Service_Object * GLIVR86Service: _make_ServiceFactory_T() active
注意:
1,需要定义宏ACE_BUILD_SVC_DLL
2,需要包含ACE/svc_export.h头文件
3,用户类必须直接或间接的派生自ACE_Service_Object
4,声明服务工厂,ACE_SVC_FACTORY_DECLARE(CLS_NAME),实际上是声明_make_CLS_NAME接口
5,实现类中必须的接口,如init、fini
6,定义服务工厂,ACE_SVC_FACTORY_DEFINE(CLS_NAME),缺少这一步,将会在调用ace_yyparse后出错,不能正常启用动态库中的服务.
ACE知识点
1、ace框架与平台选择
先看一下你的模块运行的平台,是windows/linux/unix还是其它。平台不同,使用的ACE框架还是有所差别的。比如windows下面的服务器端,一般都用Proactor框架,配合各种异步操作,如ACE_Asynch_Acceptor/ACE_Asynch_Connector,因为Proactor内部实现是完成端口,在windows平台上,公认可以取得最好的性能。
如果你用Linux,服务器端推荐你使用Reactor框架+Dev_Poll_Reactor实现,这个实现使用了Epoll机制,性能很棒。
客户端,一般为了兼容性考虑,都用Reactor,当然,如果是在windows上面运行,默认实现是WFMO_Reactor。
2、任务和主动对象(Active Object)
在主动对象持有的线程中,如果调用的是被动对象的方法(常规对象),调用会阻塞
(同步的);而另一方面,如果调用的是主动对象的方法,调用不会阻塞(异步的)。ACE_Task 是 ACE 中的任务或主动对象"处理结构"的基类。 ACE_Task 可用作:
1)更高级的线程(我们称之为任务)。
2)主动对象模式中的主动对象。
3、ACE Reactor框架类,用于检测事件的发生,并随即将这些事件多路分离和分派给它们的事件处理器。就会保证在此事件发生时,自动回调在适当的事件处理器对象中的适当的"handle"方法。
使用 ACE_Reactor基本上有三个步骤:
1、创建 ACE_Event_Handler 的子类,并在其中实现适当的"handle_"方法,以处理你想要此事件处理器为之服务的事件类型。
2、通过调用反应器对象的 register_handler(),将你的事件处理器登记到反应器。
3、在事件发生时,反应器将自动回调相应的事件处理器对象的适当的"handle_"方法。
ACE_Event_Handler是实现向Reactor注册I/O, Signal, Timeout等时间并处理的类, 通过回调函数来进行处理.handle_input, handle_output, handle_timeout, handle_signal等就是分别注册的回调函数, 我们称为handle_*回调函数,注意不包括handle_close.另外需要注意就是handle_close函数, Reactor对于handle_*回调函数的返回值是由规定的,如果返回0就表示处理正常,Reactor会继续保持事件并在发生时派发, 但是如果返回了-1, 就代表了出错, 需要删除注册对应的handle_*回调函数. handle_close()在handle_*回调函数返回-1或者我们显示调用了remove_hadler函数后被调(remove_hadler有个参数, 可以指定是否调用handle_close 指定DONT_CALL就会不调用handle_close,主要用于我们已经在handle_close函数中调用了remove_handle函数,接着操作不需要重复调用handle_close).
: handle_close()在handle_*回调函数返回-1或者我们显示调用了remove_hadler函数后(无DONT_CALL)被调用, 另Reactor会忽略hanle_close的返回值, 其他的cancel_timer默认的不调用handle_close, 但也可以强制调用, 移除事件处理器使之不再处理信号时,也不调用handle_close()清理工作: 所有的清除操作都集中写在handle_close函数中,清晰不容易出错.
4、MessageQueue消息队列
high_water_mark/low_water_mark设置消息队列的水位标(参数是所有消息块的数据大小,而不是消息块的数量),水位标用于在消息队列中指示何时在其中的数据已过多(消息队列到达了高水位标),或何时在其中的数据的数量不足(消息队列到达了低水位标)。
ACE_Message_Block ,表示消息块。每个 ACE_Message_Block 都有两个底层指针:rd_ptr 和 wr_ptr,用于在消息块中读写数据。它们可以通过调用 rd_ptr()和 wr_ptr()方法来直接访问。rd_ptr指向下一次读取数据的位置,而 wr_ptr指向下一次写入数据的位置。
5、ACE_Event_Handler,表示事件处理类
ACE_Task,表示主动对象任务
ACE_Svc_Handler,表示服务处理类,它同时继承自 ACE_Task 和 ACE_Event_Handler,并且增加了一个私有数据流。这种结合使得 ACE_Svc_Handler对象能够用作这样的任务:它能够处理事件、并与远地主机的任务间发送和接收数据。
6、ACE的动态库服务编写要点
1),需要定义宏ACE_BUILD_SVC_DLL
2),需要包含ACE/svc_export.h头文件
3),用户类必须直接或间接的派生自ACE_Service_Object
4),声明服务工厂,ACE_SVC_FACTORY_DECLARE(CLS_NAME),实际上是声明_make_CLS_NAME接口
5),实现类中必须的接口,如init、fini
6),定义服务工厂,ACE_SVC_FACTORY_DEFINE(CLS_NAME),缺少这一步,将会在调用ace_yyparse后出错,不能正常启用动态库中的服务。
实例参考上面例子。
7、进程间通信服务访问点包装
socket、TLI、STREAM管道和FIFO为访问局部和全局IPC机制提供广泛的接口。但是,有许多问题与这些不统一的接口有关联。比如类型安全的缺乏和多维度的复杂性会导致成问题的和易错的编程。
ACE的IPC SAP类属提供了统一的层次类属,对那些麻烦而易错的接口进行封装。在保持高性能的同时,IPC SAP被设计用于改善通信软件的正确性、易学性、可移植性和可复用性。
8、内存管理
ACE含有两组不同的类用于内存管理。
第一组是那些基于ACE_Allocator的类。这组类使用动态绑定和策略模式来提供灵活性和可扩展性。它们只能用于局部的动态内存分配。
第二组类基于ACE_Malloc模板类。这组类使用C++模板和外部多态性(External Polymorphism)来为内存分配机制提供灵活性。在这组类中的类不仅包括了用于局部动态内存管理的类,也包括了管理进程间共享内存的类。这些共享内存类使用底层OS(OS)共享内存接口。