当前位置: 首页 > 工具软件 > ACE > 使用案例 >

ACE介绍

章昱
2023-12-01
       ACE作为通讯方面的开源架构,不但用c++实现,而且用JAVA实作的架构已经可以使用了,由此看来掌握ACE成为每个开发通讯程序的程序员的必备技能。

ACE的库分为4个层次: 
      OS适配层 该层将ACE的较高层和与OS机制相关联的平台特有的依赖屏蔽开来。
      OO包装层 封装并增强在像Win32和UNIX这样的现代操作系统上可用的并发、进程间通信(IPC)、以及虚拟内存机制。应用可以通过有选择地继承、聚合(aggregating)、和/或实例化ACE包装类属来合并和编写这些组件
       框架    包括反应器,服务配置器,流。
       ACE 的通讯模式包括接受器-连接器,前摄器两种主要的通讯模式。前摄器理解可以理解为象Windws的Overlapper形式的一种利用操作系统的挂钩进行快速异步处理IO通讯的一种方式。它在某种程度上类似于一种软中断。用户只负责编写并注册相应的挂钩, 操作系统负责j监测事件发生,并调用相应的挂钩。接受器-连接器模式是我们经常使用的通讯模式。相对于连接器,接收器模式是服务器处理程序经常重复编写的救世主。程序员在编写服务器处理程序时,无论是采用异步通讯还是阻塞通讯,单个线程还是多个线程,都可以采用接收器方式,由此可见接受器-连接器模式的强大。接受器-连接器模式的服务器端用接收器,客户端使用连接器连接服务器(当然可以采用其他方式连接到采用接收器的服务器上),相对于连接器,接受器简化了服务端编程的复杂度,使程序员从大量重复的工作中解脱出来,并且编写出成熟稳定的服务处理程序,对比以前只有少数具有丰富的通讯程序编写经验的人才能写出稳定健壮的服务处理程序(如web服务器),ACE的接收器可以称之为改写这一历史的巨人。
      接受器模式是ACE中最闪耀之处,是通讯程序编写史上的分水岭,他的强大足以使我们震惊。
       在ACE文档方面,尽管有马维达翻译的Douglas C.Schmidt和Huston编写的《C++网络编程》卷1,卷2,还有《ACE技术论文集》,《ACE程序员教程》《ACE应用实例》,但是领悟ACE的接收器不是一件容易的事情。原因也许归于开源项目的一个通病--文档比较生僻难懂,或者不全面。所以开源项目领悟的最好方法是结合文档读源代码。   

        接收器主要有ACE_Acceptor, ACE_Svc_Handler, ACE_Reactor 3个主要类组成。ACE_Reactor是分发器(Dispatcher), ACE_Acceptor 创建出ACE_Svc_Handler.

        处理顺序是:
            1.ACE_Acceptor的open将自身帮定到ACE_Reactor上,并向其注册:当在PEER_ADDR上发生ACCEPT事件,调用handle_input成员挂钩函数。
           2.主程序调用ACE_Reactor的handle_events()时,检测到ACCEPT,调用ACE_Acceptor的handle_input()。在Handle_Input中继续调用虚函数make_svc_handle()构造出ACE_Svc_Handler类(可以新建,则每客户一个Handler,也可使用单例,则多个客户共用一个服务处理器)。接着调用accept_ svc_handle(),将具体的参数传给ACE_Svc_Handler。最后调用active_ svc_handle(),一般调用ACE_Svc_Handler的open函数。在Open函数中注册反应器事件,如必要调用active()创建出线程。   我们把创建接收器的线程称为主线程,把运行Ace_Reactor的handle_events()的线程取名为事件分发线程。把运行ACE_Svc_Handler的svc()的线程叫做服务线程。这些线程根据实现不同会有以下几种组合。l       
         主线程,事件分发线程, 服务线程 三者合一在ACE_Svc_Handler的open函数中不调用active(),则服务不创建新的线程。 主线程,事件分发线程合一,服务线程运行z在ACE_Svc_Handler的open函数中调用active(),则服务线程创建,线程运行ACE_Svc_Handler的svc()。
      主线程运行,事件分发线程和服务线程合一。后叙述
      主线程,事件分发线程,服务线程都运行在ACE_Svc_Handler的open函数中调用active();另创建一个线程,循环运行ACE_Reactor的handle_events() 或者run_event_loop();l         主线程, 服务线程合一,  事件分发线程运行。另创建一个线程,循环运行ACE_Reactor的handle_events() 或者run_event_loop();在ACE_Svc_Handler的open函数中不调用active()。 三个线程都运行时,下表显示类的成员函数被线程调用的关系.当3个线程都运行时,缺省状态下ACE_Acceptor和ACE_Svc_Handler使用一个反应器,而反应器监测事件到达时,调用ACE_Event_Handler类(ACE_Acceptor和ACE_Svc_Handler 都继承了ACE_Event_Handler)的handle_*钩子函数。因此,handle_*函数实际上运行在事件分发线程上。一般在ACE_Svc_Handler的handle_input钩子函数中读取数据,也就是说读数据在事件分发线程内执行了,这样读处理没有并行化,可能ACE的设计者认为操作系统的socket尽管有多个,实际的IO处理比如读写是串行的,因此缺省的Connector-Acceptor架构被设计成这样。Svc()运行在服务线程内,对读取的数据进行处理,并用peer()返回的Stream发送结果,这样一来,事件分发线程和服务线程会公用一个Handler,所以应当采用互斥方式访问IO。传统的Soket服务编程一般是主线程在监听soket等待一个客户端的连接,然后产生一个用于和客户端通讯的数据soket,同时创建一个线程或进程,该线程用此socket从客户端接收数据,进行数据处理,然后发送结果到客户端。这就是每客户每处理的通讯服务程序方式。如何用Connector-Acceptor架构实现这一过程呢? 如上表格所示,ACE_Svc_Handler的handle_input()应当从事件分发线程线程移到服务线程运行,因此,实现多线程同步IO的方法是将用于ACE_Acceptor的反应器和ACE_Svc_Handler的反应器分开,并且做到每个数据Soket有一个反应器,这样就可以同步访问,这种方式就是前面列出的组合的第3种组合,不过事件分发线程被分为两部分,一部分是反应器分发监听socket的事件函数即run_reactor_event_loop()函数。另一部分是反应器分发数据socket的事件函数即run_reactor_event_loop()函数。具体实现步骤如下:1. 在ACE_Svc_Handler 的open函数中, 重新为此 ACE_Svc_Handler实例分配一个新的反应器,并用此实例作为参数 注册读写,关闭事件。2. 接着调用active(),会创建出线程。3. 在ACE_Svc_Handler的svc()中循环调用此反应器的handle_events()或run_reactor_event_loop()函数。4. 在handle_input函数中读取数据,然后进行数据分析。发送写消息到反应器。会调用handle_outpu().5. 在handle_outpu()发送结果到客户端。经过这样改写,就可以用Connector-Acceptor架构实现每客户每处理的通讯方式。ACE的功能强大,配置灵活多样,以至于很多新手在实现一个程序时面对很多实现方式,一时不能决定究竟如何选择。而现有的关于ACE的书本和文档的例子都很不完整,这些都为初学ACE的程序员增加了难度。我的经验是尽量优先使用模式,然后框架,最后是类层次的复用。所以使用ACE,一定先选择Connector-Acceptor和Proactor,只有当这两者不能满足要求时再考虑使用其它的类。
 类似资料: