ACE库下载地址:http://ace.ece.uci.edu/ 或 http://www.riverace.com
ACE: Adaptive Communication Environment 自适应通信环境,属于主机基础设施中间件
第0章、简介
1.网络通信的设计空间:
1).通信空间:交互规则、形式
2).并发空间:并发控制、同步
3).服务空间:持续时间、结构
4).配置空间:网络服务识别、绑定
2.面向对象中间件体系结构的层次
1).主机基础设施中间件:对OS并发机制和进程间通信机制进行封装,以获得面向对象编程的能力。例如封装socket、posix线程
2).分布式中间件:扩展了主机基础设施中间件,使得一些网络编程(连接管理、内存管理、整编、解编端点和请求的多路分离、同步、多线程)自动化。主要管理支持面向对象分布式整编模型的终端系统资源。分布式中间件的核心是ORB,COM++,JAVA RMI,CORBA
3).公共中间件:扩展了分布式中间件,独立于特定服务,主要对整个分布式系统中的各种资源进行分配、调度和协调
4).特定领域中间件:满足特定领域的特定需求
3.主机基础设施中间件的优势
满足QoS需求:相比于分布式中间件,主机基础设施中间件吞吐量和延迟上的开销小,可以解决抖动和可靠性
主机基础设施中间件允许程序:
*忽略不必要的功能:如同环境中的整编和解编
*对通信行为施加精细的控制:如支持IP多路传输和异步I/O
*定制网络协议,以对网络带宽的使用实施优化,或将共享内存通信替换为回送网络通讯
第一章、通信设计空间
.无连接协议和面向连接协议:无连接提供面向消息的服务,如语音、视频允许一定的数据丢失;面向连接提供可靠的服务,适于不允许数据丢失的服务。
当使用面向连接协议时,设计者需要注意以下问题:
*数据成帧策略:面向连接提供了多种数据成帧策略,如面向消息发送策略被某些面向连接协议所支持如TP4,XTP。而TCP市一中字节流协议,不
保护应用程序消息的边界,例如在TCP上,如果某一应用程序通过4个send()调用传输4条不同的消息,那么会有1个或多个(可能>4个)TCP数据段
被传输给接收端。所以如果某一应用程序需要面向消息发送,发送端和接收端就必须执行额外的处理,以将4条在TCP上交换的消息分割成帧。如
果消息的总长度总相同,并且永远没有网络错误,成帧相对来说简单,否则就会成为一个不小的问题。选择了TCP就要在TCP字节流上实现分帧机制
*连接多路复用策略(非I/O的多路复用):在面向连接协议上传输数据,有2个一般性的策略:多路复用(一个进程的所以线程发出的所有客户
请求都通过1条TCP连接传递给一个服务器进程。优点:节省OS通讯资源;缺点:难以控制难以编程缺乏效率和确定性)和非多路复用(每一个客
户都是用不同的额连接和对等服务程序通讯。优点:可以更好的控制通讯的优先级,且同步开销小)
.同步及异步消息交换:管理请求/应答协议交换的可选机制有2种:同步和异步消息交换。同步请求/应答协议中请求和应答是以锁步的次序交换的,每一个
请求必须同步接收到一个应答,然后才能发送下一个请求。异步请求/应答协议中每一个消息是独立的,但是异步请求往往需要一种策略来检测请求
的丢失或失败,然后重新发送。异步适用于“通信延迟”和“请求所需的处理时间”密切相关的场合。
.消息传递与共享内存实现数据交换:消息传递明确的通过IPC机制来交换字节流和面向记录的数据。消息传递IPC机制通过IPC信道,将数据以消息的形式从
一个进程或线程传递到另一个进程或线程。如果数据很大,这些数据就会分片以消息序列的形式发送。如果有一个以上的进程接收数据,则每一条
消息就要发送多次,每一次针对一个接收者。譬如RPC、CORBA和面向消息中间件(MOM),其内部都是基于消息传递模型。
内存共享:允许相同或不同主机上的多个进程访问、交换数据就像数据位于每一个进程的本地地址空间一样。在网络应用程序中,如果数据必须被
多个进程读取和处理,那么较之“消息传递”则“内存共享”设施是一种更有效的通信机制。共享内存有本地和分布式内存共享2种:
本地共享内存(LSM):允许同一主机上的进程拥有一个或多个共享内存
分布式共享内存(DSM):DSM在网络上扩展了虚拟内存的概念,以通过全局/共享内存中的数据进行透明的进程通信。
第二章、Socket API概述(IPC)
1.操作系统IPC机制分为2种:本地IPC和远程IPC。本地IPC:共享内存、管道、AF_UNIX域Socket、门door、信号等
远程IPC:AF_INET域Socket、X.25电路、命名管道
2.Socket接口:每一个Socket可以绑定至一个本地地址和一个远程地址。Socket API大约有20多个系统函数,这些函数可以分为以下5类:
1).本地环境管理:socket(工厂函数,用于分配socket句柄) bind(将socket绑定到本地或远程地址) getsockname(返回socket绑定的本地地址) getpeername(返回socket绑定的远程地址) close(释放socket句柄,使其可复用)
2).连接的建立与终止:connect(主动在一个socket句柄上建立连接) listen(表示愿意被动侦听来自客户的连接请求) accept(工厂函数。响应客户请求,创建一个新的连接) shutdown(有选择的终止双向连接中读取方和写入方的数据流)
3).数据传输机制:send recv(通过某一特定的I/O句柄,传送和接收数据缓冲区数据) sendto recvfrom(交换无连接数据报)
read write(通过某一句柄,接收和传送数据缓冲区) readv writev(分别支持“分散读取”和“集中写入”,以优化模式切换,简化内存管理。) sendmsg recvmsg(通用函数,包含其他其他数据传输函数的行为)
4).选项管理:setsockopt(在协议的不同层修改选项) getsockopt(在协议的不同层查询选项)
5).网络地址:gethostbyname gethostbyaddr(处理主机名和IPv4地址之间的网络地址映像) getipnodebyname getipnodebyaddr(处理主机名和IPv4/IPv6地址之间的网络地址映像) getservbyname(通过具有可读性的名称标识服务)
Socket API的局限性:容易出错、过于复杂、不可移植
第三章、ACE Socket Wrapper Facade
ACE 定义了一组C++类,这类都是根据Wrapper Facade模式设计的,将非C接口封装在了面向对象的接口中。
本章提供的ACE Wraper facade类有:
ACE_Addr : ACE网络地址继承结构的根
ACE_INET_Addr :封装了AF_INET域的地址族(专门初始化INET域的地址,其他域的地址用ACE_Addr初始化)
ACE_IPC_SAP :ACE IPC wrapper facade 继承结构的根
ACE_SOCK :ACE Socket wrapper facade 继承结构的根
ACE_SOCK_Connector:连接工厂,连接到一个对等的接受者,然后在一个ACE_SOCK_Stream 对象中初始化一个新的通信端点
ACE_SOCK_IO :封装了”数据模式“socket支持的数据传输机制。
ACE_SOCK_Stream :同上
ACE_SOCK_Acceptor :接收连接工厂,在一个ACE_SOCK_Stream对象中初始化一个新的通信端点,对来自对等连接者的请求做出响应。
#ACE_Addr和ACE_INET_Addr
客户端和服务器的ACE_Addr地址都可以使用sap_any:
*客户端可以使用sap_any来创建OS分配的临时端口号,在连接关闭之后,这些端口可以再次使用
*服务器程序可以通过sap_any选择他们的端口号,只要他们通过某种定位机制向客户输出了被分配的端口号
#ACE_IPC_SAP
ACE_IPC_SAP是ACE IPC wrapper facade 继承结构的根,它为其它ACE wrapper facade提供了基本的I/O(句柄)操作能力
它的目的不是给应用程序直接使用,而是提供子类譬如对文件、STREAM管道、命名管道、System V传输层接口(TLI)的ACE Wrapper facade使用。
#ACE_SOCK_Connector
该类是一个工厂类,用以主动建立一个新的通信端。对给定的服务器地址进行连接,并返回一个ACE_SOCK_Stream对象供应用程序读写
支持”阻塞“、”非阻塞“、”定时“
#ACE_SOCK_Stream
该类负责数据的接收和发送,提供了多种send和recv方法变体(包括”分散读取,集中写入“的方法)。支持”阻塞“、”非阻塞“、”定时“
分散读取和集中写入的作用:
分散/聚集I/O对于将数据划分为几个部分很有用。例如,您可能在编写一个使用消息对象的网络应用程序,每一个消息被划分为固定
长度的头部和固定长度的正文。您可以创建一个刚好可以容纳头部的缓冲区和另一个刚好可以容难正文的缓冲区。当您将它们放入一
个数组中并使用分散读取来向它们读入消息时,头部和正文将整齐地划分到这 两个缓冲区中。
#ACE_SOCK_Acceptor
该类封装了底层的socket、bind、listen用来被动接收一个对等端连接,并在连接建立后,返回一个ACE_SOCK_Stream对象,供服务器读写
支持”阻塞“、”非阻塞“、”定时“
#ACE_Mem_Map
ACE_Mem_Map的基础是”内存映射文件”机制。使用了OS虚拟内存机制,将文件地址映射到进程地址空间中。被映射的文件内容可以通过指针
直接访问。“内存映射文件”可以被同一台主机上的多个进程共享。
ACE_Mem_Map map_file("D://LK.TXT");
send_n(map_file.addr(),map_file.size());
第四章、网络日志服务器的实现
#ACE_Message_Block(简单消息块(链))
允许多条消息连在一起,形成一条单链表,从而支持复合消息。
int main()
{
ACE_Message_Block *head = new ACE_Message_Block(BUSSIZE);
ACE_Message_Block *mblk=head;
while(true)
{ int nbytes=ACE::read_n(ACE_STDIN,mblk->wr_ptr(),mblk->size());
if(nbytes<=0)
break;
mblk->wr_ptr(nbytes);
mblk-cont(new ACE_Message_Block(BUSSIZE));//链接新的一个消息块
mblk=mblk->cont();
}
mblk=head;
while(mblk)
{
ACE::write_n(ACE_STDOUT,mblk->rd_ptr(),mblk->length());
mblk->cont();
}
head->release(); //释放所有消息块
return 0;
}
#ACE_Message_Blocks(复合消息块(链))
#ACE_InputCDR ACE_OutputCDR (CDR:Common Data Representation 公共数据表示)
作用:统一不同主机不同环境的“字节序”规则,将类型化的数据和数据流相互转换。只提供原始数据类型及其数组的整编和解编。
原始数据类型包括:bool char int16 int32 int64 float double 字符 字符串
ACE_OutputCDR 根据数据结构创建CDR缓冲区,将数据保存在缓冲区中->整编
ACE_InputCDR 从CDR缓冲区中提取数据->解编
第五章、并发设计空间
并发设计空间涉及使用多进程、多线程及其同步装置的策略和机制。
服务器分类:循环式、反应式、并发式。
循环式服务器:在处理后续的请求之前,会完整的处理每一个客户请求。因此在处理一个请求时,要将其它请求排成队列。
适用于:短期服务、不经常运行的服务器。
缺点:当客户端因为等待服务器处理请求而被阻塞时,会阻止客户程序继续向下运行。如果服务器延迟太久,
则应用程序和中间件层中用于”重新传输“的”超时“计算会变得复杂,从而引发严重的网络阻塞;并且根
据客户和服务器交换请求时使用的协议类型,服务器还有可能接收到重复请求。
并发式服务器:使用多线程或多进程,同时处理多个客户请求。如果是”单服务“服务器,则同一服务的多个副本可以同时运行。
如果是”多服务“服务器,则不同服务的多个副本可以同时运行。
适用于:“I/O操作频繁”的服务或执行时间会变化的长周期服务。
优点:可以使用更精细的同步技术,能在“应用程序定义的层次”将“请求”串行化。这种机制需要使用同步机制如信号量、互斥锁,以
保证同时运行的进程和线程之间的稳固合作和数据共享。
反应式服务器(同步事件多路分离机制):几乎可以同时处理多个请求(尽管所有的处理实际上都在一个线程中完成,当请求到达时也可分离出相应的线程专门处理请求。)
缺点:如果一个操作失败(例如死锁或挂起),则整个服务器进程都会挂起。
OS的线程调度模型:
N:1 用户线程模型
1:1 核心线程模型
N:M 混合线程模型
OS的不同线程调度模型中线程的竞争范围:
进程竞争范围(用户空间):与同一进程中的线程共同竞争CPU资源
系统竞争范围(内核空间):与其他进程中的线程共同竞争CPU资源
OS的2个调度级别:
分时调度级别(不同优先级)
*基于优先级
*公平
*抢占
实时调度级别(同优先级)
*轮流
*先进先出
*时间片
并发体系有2种模型:
“基于任务”的并发体系:根据应用程序中的“服务功能单元”来组织多个CPU。在这一体系中,任务是主动的
而任务处理的消息是被动的。并发性是通过在各个CPU中执行服务任务,并在任务/CPU之间传递数据消
息和控制消息而获得。“基于任务”的并发体系可以通过“生产者/消费者”模式来实现。
“基于消息”的并发体系:从应用程序和网络设备接收到的消息来组织CPU。在这一体系中消息是主动的,
任务是被动的。并发性是借助一个服务任务栈,通过同时在各个CPU上处理多个消息来获得。”一个请求一个线程“、
”一个连接一个线程“、”线程池“模型可以用来实现”基于消息“的并发体系。
第六章、操作系统并发机制
”同步事件多路分离“:select/Poll用于在一组事件源上等待特定事件的发生。当某个(或多个)事件源被激活时,函数将返回至调用者。于是,调用者就可以处理这些”来自多个源“的时间。同步事件多路分离是反应式服务器的基础。
互斥锁:可以用来串行执行多个线程。互斥锁有2种:
*递归互斥锁:拥有互斥锁的线程可以多次获得它而不会产生死锁,只要这个线程最终以相同的次数释放这个互斥锁即可
*非递归互斥锁:如果当前拥有互斥锁的进程在没有首先释放它的情况下,仕途再次获得它,就会导致死锁而失败。
读写锁:
信号量锁:
条件变量:
第七章、ACE同步事件多路分离(Wrapper Facade)
网络应用程序中的事件源主要是socket句柄。select可以管理事件源。
#ACE_Handle_Set 封装了select可以管理的所有句柄
该类利用wrapper facade模式来指导fd_set的封装,提供了对句柄操作的方法。
select管理的句柄一定要设置为非阻塞模式,否则有可能程序永远被挂起
反应式多路分离执行步骤:
*select函数返回被修改的句柄集
*服务器的事件循环搜索活动句柄集,并针对每一个活动句柄执行”时间处理“代码
#ACE_Handle_Set_Iterator
ACE_Handle_Set_Iterator用select返回的活动句柄集构造迭代对象,用以用以高效的搜索select返回的活动句柄集,并且每次迭代只返回一个活动句柄,直到ACE_INVALID_HANDLE
第八章、ACE进程Wrapper Facade
多进程适用于:
*不可能使用多线程方案的地方
*不适合使用多线程方案的地方-受”不可重入“方法的影响
优点:
*多进程比多线程稳固
#ACE_Process
可移植的创建和同步进程,以及保存和访问进程属性(如进程ID)
#ACE_Process_Options
指定与平台无关和与平台相关的选项
#ACE_Process_Manager
可移植的创建和同步多组进程
例子:在当前进程中,启动一个子进程
ACE_Process_Option opts;
ACE_Process child;
opts.command_line("%s %d","./Main.EXE",10); //将需要执行的进程名放在ACE_Process_Option里面
child.spawn(opts);
child.wait();
return child.exit_code();
#ACE_Process_Option
作用:
*command_line() 指定要运行的进程映像及其参数
*setenv() 指定环境变量,将其添加到被运行进程的环境中
*working_directory() 为新进程指定一个新的工作路径
*set_handles() 设置新进程的STDIN,STDOUT,STDERR
*Pass_handl() 传递一个句柄给新进程
*creation_flags() 指定是否在被创建进程中运行新的程序映像
...
#ACE_Process_Manager
……保存内部记录,管理和监视ACE_Process类创建的多组进程
……允许一个进程创建一组进程
*open() 初始化ACE_Process_Manager
*close() 释放所有资源(不等待进程退出)
*spawn() 创建一个进程,将其添加到被管理的一个进程组zhong
*spawn_n() 创建n个进程,这些进程属于同一个进程组
*wait() 等待进程组中的一些或全部进程退出
*instance() 返回一个指向于ACE_Process_Manager singleton的指针的静态
第九章、ACE线程 Wrapper Facade
#ACE_Thread_Manage
……使得应用程序能够创建和管理线程的生存期、同步和属性
*spawn()创建一个新的控制线程,并传达给它一个"函数"和"函数参数",作为线程执行的入口
*spawn_a()创建n个"同属于一个线程组"的新线程,其他线程会等待这个线程组退出
*wait()阻塞,直到ACE_Thread_Manage中所有线程退出
*join()等待某一线程退出,并获得其退出状态
*cancel_all()请求ACE_Thread_Manager对象管理的线程全部退出
*testcancel()询问某一线程是否已被请求退出
*exit()退出一个线程,并释放这个线程的资源
*close()关闭并释放所有被管理线程的资源
*instance()返回一个指向ACE_Thread_Manage singleton的指针
#ACE_Sched_Params
……封装了OS调度类的特性,可以结合ACE_OS::sched_params()包装方法一起使用,以控制实时线程的各种属性
*ACE_Sched_Params()构造实时线程的调度策略、优先级、范围和量
*priority_min()返回某一调度策略和范围的最低优先级
*priority_max()返回某一调度策略和范围的最大优先级
*next_priority()给定一个策略、优先级和范围,返回下一个较高的优先级
*previous_priority()给定一个策略、优先级和范围,返回下一个较低的优先级
#ACE_TSS(Thread_specific storage)线程专有存储-相当于智能指针 (模板容器,使得某个静态变量为线程安全的,在每一个线程中都有一份拷贝)
……包装了线程专有存储机制,使得在物理上私有于某一线程的对象可以被全局访问
第十章、ACE同步Wrapper Facade
#ACE_Guard
#ACE_Read_Guard运用Scoped Locking技术,确保进入和退出一段C++代码时,能够自动得到或释放锁。用{}来确定范围
#ACE_Write_Guard
#ACE_Thread_Mutex
#ACE_Process_Mutex为并发应用程序提供一个简单高效的机制,用以串行访问共享资源
#ACE_Null_Mutex
#ACE_RW_Thread_Mutex 可以高效、并发的访问"内容经常被读取但很少被修改"的资源
#ACE_RW_Process_Mutex
#ACE_Thread_Semaphor
#ACE_Process_Semaphore实现"计数信号量"
#ACE_Null_Semaphore
#ACE_Condition_Thread_Mutex使得线程可以高效的协调和调度他们的处理
#ACE_Null_Condition
第 1章 ACE 自适配通信环境
1.ACE 中的组件可用于以下几种目的:
a.并发和同步
b.进程间通信(IPC)
c.内存管理
d.定时器
e.信号
f.文件系统管理
g.线程管理
h.事件多路分离和处理器分派
i.连接建立和服务初始化
j.软件的静态和动态配置、重配置
k.分层协议构建和流式框架
l.分布式通信服务:名字、日志、时间同步、事件路由和网络锁定,等等。
2.在 ACE 框架中有三个基本层次:
a.操作系统(OS)适配层
b.C++包装层
c.框架和模式层
3.ACE 框架组件
a.事件处理
b.连接或服务初始化组件
c.流组件
d.服务配置组件
第 2章 IPC SAP:进程间通信服务访问点包装
1.IPC SAP 类被划分为四种主要的类属:ACE_SOCK、 ACE_TLI、 ACE_SPIPE、 ACE_FIFO
ACE_SOCK类属之下;它提供使用 BSD socket 编程接口的Internet 域和UNIX域协议族的接口。这个类属中的类被进一步划分为:Dgram类和 Stream类;Acceptor、 Connector类和Stream类
第 3章 ACE 的内存管理
1.ACE 含有两组不同的类用于内存管理:
第一组类是那些基于 ACE_Allocator的类。这组类使用动态绑定和策略模式来提供灵活性和可扩展性。它们只能用于局部的动态内存分配。
第二组类基于 ACE_Malloc 模板类。这组类使用 C++模板和外部多态性 (External Polymorphism)来为内存分配机制提供灵活性。在这组类中的类不仅包括了用于局部动态内存管理的类,也包括了管理进程间共享内存的类。这些共享内存类使用底层 OS(OS)共享内存接口。
第 4章 线程管理:ACE 的同步和线程管理机制
1.3种建立线程的机制:
ACE_Thread、ACE_Thread_Manage、ACE_Task
ACE_Thread提供了所有静态方法的形式创建线程;ACE_Thread_Manage单体实现,便于对线程及线程组进行管理;ACE_Task提供面向对象的线程编程。
2.ACE 同步原语
ACE Lock 类属 包括10几种不同的锁
ACE Guard类属
ACE Condition 类属
ACE_Event类
杂项 ACE Synchronization类
ACE_Thread::self()返回当前线程的线程号。
3.ACE同步一些基本的功能:
linux IPC进程间通信SAP、system V IPC封装;POSIX pthreads 和 Win32 线程都实现了递归和非递归互斥体;Token(令牌) :Token 类提供一种比 Mutex 更为通用的同步机制。例如,它实现了“递归互斥体”语义,拥有令牌的线程可以重新获取它,而不会导致死锁。此外,当其他线程释放令牌时,阻塞在该令牌上的线程以严格的 FIFO(先进先出)的顺序被服务(相反,Mutex 并不严格地强制实行一种获取顺序);条件变量与其它同步机制的不同(主动等待与被动等待)条件变量提供风格与互斥体、读/写锁和计数信号量不同的锁定机制。当持有锁的线程在临界区执行代码时,这三种机制让协作线程进行等待。相反,条件变量通常被一个线程用于使自己等待,直到一个涉及共享数据的条件表达式到达特定的状态。当另外的协作线程指示共享数据的状态已发生变化,调度器就唤醒一个在该条件变量上挂起的线程。于是新唤醒的线程重新对它的条件表达式进行求值,如果共享数据已到达合适状态,就恢复处理。 对于涉及条件表达式语义的情况,条件变量比信号量或互斥体要更为有用。条件变量语义特别适合用于实现生产者/消费者模型.
4.ACE_Thread、ACE_Thread_Manager和ACE_Task的主要应用
ACE_Thread::spawn((ACE_THR_FUNC)worker, (void*)&arg,THR_DETACHED|THR_NEW_LWP) 该函数可以直接将一个函数创建为线程。类似于ACE_Thread_Manager::instance()->spawn()ACE_Thread线程类提供的全是静态线程管理方法,ACE_Thread_Manager是ACE_Thread的超集,这2种类都不便于实现面向对象的编程,因为他们是使一个函数或静态方法线程化来实现启动线程。而通过从ACE_Task派生的线程可以实现全面的面向对象。
通过这个获得ACE_Thread_Manager::instance()线程管理器实例,因为线程管理器只能有一个,但可以通过设置线程组ID管理多组线程ACE_Thread_Manager::instance()->spawn_n()可以使用ACE_Thread_Manager::instance()->wait()等待所有的线程执行完成。
5.posix线程专有存储的实现TSS
TSS线程专有存储-thread specific save,线程专有存储数据实现线程对一个类中所有方法的互斥访问(将全局数据放入线程专有存储中),而原子操作类SCE_Atomic_Op类只能实现对普通数据类型的互斥访问。
ACE_TSS
ACE_TSS
static void* thread1(void*)
{
data->set(10);
ACE_DEBUG((LM_DEBUG,"(%t)The value of data is %d \n",data->get()));
for(int i=0;i<5;i++)
data->increment();
ACE_DEBUG((LM_DEBUG,"(%t)The value of data is %d \n",data->get()));
return 0;
}
static void * thread2(void*)
{
data->set(100);
ACE_DEBUG((LM_DEBUG,"(%t)The value of data is %d \n",data->get()));
for(int i=0; i<5;i++)
data->increment();
ACE_DEBUG((LM_DEBUG,"(%t)The value of data is %d \n",data->get()));
return 0;
}
第5章 任务和主动对象(Active Object) :并发编程模式
1.主动对象和任务
主动对象持有自己的线程,并将这个线程用于执行对它们的任何方法的调用。因而,如果一个传统对象,在里面封装了一个线程或多个线程,它就是一个主动对象。
任务:每个任务都含有一或多个线程,以及一个底层消息队列。各个任务通过这些消息队列进行通信。但是,消息队列并非是程序员需要关注的对象。发送任务可以使用 putq()调用来将消
息插入到另一任务的消息队列中。随后接收任务就可以通过使用 getq()调用来从它自己的消息队列里将消息提取出来。
2.创建和使用任务
a.实现服务初始化和终止方法:open()方法应该包含所有专属于任务的初始化代码。其中可能包括诸如连接控制块、锁和内存这样的资源。open()方法需要用户主动调用。close()方法是相应的终止方法。 close()方法在线程结束或终止时自动被调用。
b.调用启用(Activation)方法:在主动对象实例化后,你必须通过调用 activate()启用它。要在主动对象中创建的线程的数目,以及其他一些参数,被传递给 activate()方法。activate()方法会使 svc()方法成为所有它生成的线程的启动点。 activate()方法需要用户主动调用。
c.实现服务专有的处理方法:如上面所提到的,在主动对象被启用后,各个新线程在 svc()方法中启动。应用开发者必须在子类中定义此方法。 svc()方法在用户调用了activate()方法后将被自动调用。
3.主动对象模式(Active Object Pattern)
该模式有如下参与者:
a.主动对象(基于 ACE_Task) 。
b.ACE_Activation_Queue。
c.若干 ACE_Method_Object(主动对象的每个方法都需要有一个方法对象) 。
d.若干 ACE_Future 对象(每个要返回结果的方法都需要这样一个对象) 。
第6章 反应器(Reactor) :用于事件多路分离和分派的体系结构模式
1.反应器还将若干不同种类的事件的多路分离集成到易于使用的 API 中。特别地,反应器对基于定时器的事件、信号事件、基于 I/O 端口监控的事件和用户定义的通知进行统一地处理。
整个反应堆基于2个对象:ACE_REACTOR、ACE_Event_Handle
2.反应堆使用步骤:
a.实现ACE_Event_Handler 的子类,并在其中实现适当的“handle_”方法
b.调用反应器对象的 register_handler()或schedule_timer,将事件登记到反应器等待队列;或直接调用->notify(eh,掩膜)将eh事件插入就绪队列。
c.调用事件循环。 while(1){ACE_Reactor::instance()
->handle_events();}
第 7章 接受器(Acceptor)和连接器(Connector) :连接建立模式(工厂+反应器模式)
1.连接器和接收器:
ACE_Acceptor或ACE_onnector被实现为模板容器,通过两个类作为实参来进行实例化。第一个参数实现特定的服务(类型为 ACE_Event_Handler。因为它被用于处理
I/O 事件,所以必须来自事件处理类层次) ,应用在建立连接后执行该服务;第二个参数是“具体的”接受器或连接器(可以是在 IPC_SAP 一章中讨论的各种变种)。
typedef ACE_Acceptor
2.具体服务处理器ACE_Svc_Handler->My_Service_Handler
由应用开发者编写,它的 open()方法在连接建立后被自动回调。接受器框架假定服务处理类的类型是 ACE_Event_Handler,这是 ACE 定义的接口类(该类已在反应器一章中详细讨论过)。另一个特别为接受器和连接器模式的服务处理而创建的类是 ACE_Svc_Handler。该类不仅基于ACE_Event_Handler接口 (这是使用反应器所必需的) , 同时还基于在 ASX 流框架中使用的 ACE_Task类。ACE_Task 类提供的功能有:创建分离的线程、使用消息队列来存储到来的数据消息、并发地处理它们,以及其他一些有用的功能。
3.接收器和反应器工厂模式设计
接收器和连接器设计模式:工厂+反应器设计模式
接收器和连接器都可以单独使用,而不需要使用工厂+反应器设计模式。
接收器和连接器的工厂+反应器设计模式有一下优点:
a.无需连接器和接收器进行阻塞,但是需要一个反应器事件循环;
b.ACE_Event_Handler能够同时处理连接事件和流的读写事件;
c.通过事件的方式处理到来的连接事件,反应更加快速。
ACE_Acceptor接受器工厂,能够将自身隐含的注册到反应器,当有连接事件完成时,对应的事件处理对象中的handle_input()方法被调用。底层可以指定为ACE_SOCK_Acceptor或ACE_LSOCK_Acceptor
ACE_Connector连接器工厂,能够将自身隐含的注册到反应器,当有连接事件完成时,对应的事件处理对象中的handle_input()方法被调用。底层可以指定为ACE_SOCK_Connector或ACE_LSOCK_Connector
ACE_Acceptor和ACE_Connector工厂其实是一个委托,可以封装不同的底层接受器和连接器,只是提供了统一的访问接口。
ACE_Event_Handler事件处理器类是各种用户自定义事件的子类,用户可以用该类来实现文件可读事件处理器、文件可写事件处理器、连接/接受事件处理器、定时事件处理器、信号事件处理器、通知
事件处理器等,每一个具体的事件处理器其实就是继承了ACE_Event_Handler类并封装了一个文件、连接/接受器等。事件处理器用于反应器中,一个反应器需要一个或多个事件处理器(事件处理器没有
其它的用途了)。每一个事件处理器都有一个底层流,可以使用ACE_Event_Handler的peer()方法来获取,同样可以使用该方法来为ACE_Event_Handler对应的句柄(流)赋值。
ACE_Event_Handler这个需要2方面的实现:a.处理谁的事件,对应它的底层流或句柄;b.如何处理事件,实现相应的方法。 综合起来:处理谁的事件,如何处理。只要满足了这2个条件就可以把它注
册到反应器里面去了。反应器是通过get_handle()函数知道要处理发生在哪个句柄对象上的事件的。
ACE_Svc_Handler类模板继承于ACE_Event_Handler和ACE_Task,根据需要实现ACE_Event_Handler和ACE_Task的相关方法,专门用于处理网络通信事件;当有连接到达时,反应器调用其open()方法,都
有流可读写时,调用其handle_input()方法。ACE_Svc_Handler能自动更新get_handle的返回值。
接收器一般的实现方式:
class My_Svc_Handler : public ACE_Svc_Handler
ACE_Acceptor
while(1)
ACE_Reactor::instance()->handle_events();
第 8章 服务配置器(Service Configurator):用于服务动态配置的模式
1.ACE 中的服务配置器由以下组件组成:
a.名为 ACE_Service_Object 的抽象类。应用开发者必须从它派生出子类,以创建他自己的应用特有的具体服务对象(Service Object) 。
b.应用特有的具体服务对象。
c.服务仓库 ACE_Service_Repository。它记录服务器所运行的和所知道的服务。
d.ACE_Service_Config。它是整个服务配置器框架的应用开发接口。
e.服务配置文件。该文件含有所有服务对象的配置信息。其缺省的名字是 svc.conf。当你的应用对ACE_Service_Config 发出open()调用时,服务配置器框架会读取并处理你写在此文件中的所有配置信息,随后相应地配置应用。
ACE_Service_Object 包括了一些由框架调用的方法,用于服务要启动(init()) 、停止(fini()) 、挂起( suspend() ) 或 是恢复 ( resume() )时。 ACE_Service_Object 派生
自 ACE_Shared_Object 和ACE_Event_Handler。ACE_Shared_Object 在应用想要使用操作系统的动态链接机制来进行加载时被用作抽象基类。ACE_Event_Handler已在对反应器的讨论中进行
了介绍。当开发者想要他的类响应来自反应器的事件时,他就从 ACE_Event_Handler派生他的子类。
2.实现动态服务器配置的步骤:
1.编写服务器代码:继承ACE_Service_Object(或ACE_Task或ACE_Task_Base),实现其中的init/fini/suspend/resume/svc方法,并定义服务器对象
2.编写配置文件svc.conf,指明要如何执行服务
3.启动服务配置器:编写main函数,实现反应器通过发一个信号,调用ACE_Service_Config::reconfigure(); 重新配置服务器。
3.使用服务管理器
ACE_Service_Manager 是可用于对服务配置器进行远程管理的服务。它目前可以接受两种类型的请求。其一,你可以向它发送“help”消息,列出当前被加载进应用的所有服务。其二,你可以向服务管理器发送“reconfigure”消息,从而使得服务配置器重新配置它自己。 在远程端需要在指定的端口启动ACE_Service_Manager,他是一个已经实现的对象‘然后在本地就可以发送"reconfigure"信息对远程主机上的服务进行重新配置。下面的命令指定在 9876 端口静态地启动服务管理器:(配置文件中)
static ACE_Service_Manager “-p 9876”
ACE_Service_Manage 可以接收sock消息,对ACE_Service_Configure进行远程管理,用户可以编写一个客户端,向9876端口发送“reconfigure”字符.
第 9章 消息队列(Message Queue)
1.ACE_Message_Block
ACE_Message_Block可以被放在ACE_Message_Queue中。
ACE_Message_Block可以管理用户指定的内存(数组),也可以隐式的自己分配内存来存储数据。
2.ACE_Message_Block其中一个构造函数
ACE_Message_Block (size_t size,
ACE_Message_Type type = MB_DATA,
ACE_Message_Block *cont = 0,
const char *data = 0,
ACE_Allocator *allocator_strategy = 0,
ACE_Lock *locking_strategy = 0,
u_long priority = 0,
const ACE_Time_Value & execution_time = ACE_Time_Value::zero,
const ACE_Time_Value & deadline_time = ACE_Time_Value::max_time);
a.要与消息块相关联的数据缓冲区的大小。注意消息块的大小是 size,但长度将为0,直到 wr_ptr被设置为止。这将在后面进一步解释。
b.消息的类型。(在 ACE_Message_Type 枚举中有若干类型可用,其中包括缺省的数据消息)。
c.指向“片段链” (fragment chain)中的下一个消息块的指针。消息块可以实际地链接在一起来形成链。随后链可被放入消息队列中,就好像它是单个数据块一样。该参数缺省为0,意
味着此块不使用链。
d.指向要存储在此消息块中的数据缓冲区的指针。如果该参数的值为零,就会创建缓冲区(大小由第一个参数指定),并由该消息块进行管理。当消息块被删除时,相应的数据缓冲区也被
删除。但是,如果在此参数中指定了数据缓冲区, 也就是, 参数不为空, 当消息块被销毁时它就不会删除数据缓冲区。这是一个重要特性,必须牢牢记住。
e.用于分配数据缓存(如果需要)的 allocator_strategy,在第四个参数为空时使用(如上面所解释的)。任何 ACE_Allocator 的子类都可被用作这一参数。
f.如果 locking_strategy 不为零,它就将用于保护访问共享状态(例如,引用计数)的代码区,以避免竞争状态。
g.priority设置优先级;后面两个参数用于 ACE 中的实时消息队列的调度,如果将该消息块放入ACE_Dynamic_Message_Queue中,那么后2个参数将会产生作用。
3.ACE_Message_Block的消息插入与读取
每个 ACE_Message_Block 都有两个底层指针:rd_ptr 和 wr_ptr,用于在消息块中读写数据。它们可以通过调用 rd_ptr()和 wr_ptr()方法来直接访问。rd_ptr指向下一次读取数据的位
置,而 wr_ptr指向下一次写入数据的位置。程序员必须小心地管理这些指针,以保证它们总是指向正确的位置。在使用这些指针读写数据时,程序员必须自己来增加它们的值,它们不会魔
法。般地自动更新。
4.消息队列
ACE 有若干不同类型的消息队列,它们大体上可划分为两种范畴:静态的和动态的。静态 队列是一 种通用的 消息队列 ( ACE_Message_Queue ),而动 态消息队列(ACE_Dynamic_Message_Queue)是实时消息队列。这两种消息队列的主要区别是:静态队列中的消息具有静态的优先级,也就是,一旦优先级被设定就不会再改变;而另一方面,在动态消息队列中,基于诸如执行时间和最终期限等参数,消息的优先级可以动态地改变。不管是哪种消息队列保存的都是ACE_Message_Block,只是调度的方式不同。
第十章 ACE 常用类
1.什么是类型安全?
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图访问自己没被授权的内存区域。“类型安全”常被用来形容编程语言,其根据在于该门编程语言是否提供保障类型安全的机制;有的时候也用“类型安全”形容某个程序,判别的标准在于该程序是否隐含类型错误。类型安全的编程语言与类型安全的程序之间,没有必然联系。好的程序员可以使用类型不那么安全的语言写出类型相当安全的程序,相反的,差一点儿的程序员可能使用类型相当安全的语言写出类型不***全的程序。绝对类型安全的编程语言暂时还没有。C语言的类型安全,C只在局部上下文中表现出类型安全,比如试图从一种结构体的指针转换成另一种结构体的指针时,编译器将会报告错误,除非使用显式类型转换。然而,C中相当多的操作是不安全的。ACE实现了类型安全。
2.ACE基本函数及头文件
"ace/OS.h"
ACE_OS:: 静态类,包含很多CAPI ACE_OS::exit();ACE_OS::sprintf(...);ACE_OS::sleep(...)
ACE_INET_Addr AF_INET域地址
ACE_UNIX_Addr AF_UNIX域地址
ACE_Time_Value 唯一的一个时间类
ACE_DEBUG和 ACE_ERROR 宏 用于打印调试及错误信息及记录相应的日志
ACE_ERROR_RETURN(...)
ACE_Get_opt 获取命令行以‘-’引导的参数 如 ./m.exe –a –b 10 –c
ACE_Arg_Shifter 将已知的参数或选项移动到 argv 向量的后面,这样在进行更深层次的参数解析时,就可以在 argv 向量的开始处定位还没有处理的参数。也可以用于参数解析
ACE_TEXT 字符串类
ACE_CHAR 字符类
ACE_Arg_Shifter arg_shifter(argc,ACE_TCHAR *argv[]); 参数移位类
ACE_Get_Opt get_opt(argc,argv,ACE_TEXT("w:n"));获取输入的-m -n选项 获取选项类
ACE 常用的类和样板
ACE_OS::strcasecmp 大小写比较
ACE_OS::strncasecmp n个字符大小写比较
ACE::execname (prog1); 执行prog1程序
ACE_OS_String::strdup 字符串深拷贝
ACE_OS::uname (&uname); 获取操作系统信息
ACE_Copy_Disabled 非拷贝基础类
ACE_DLL 动态库类
ACE_Process_Options
ACE_Env_Value 环境变量类
ACE_Obstack_T
ACE_Ptr 指针类
ACE_Refcounted_Auto_Ptr 指针引用计数与auto_ptr相同
ACE_Refcounted_Auto_Ptr_Rep
ACE_Auto_Basic_Ptr
ACE_Vector 提供了STL相似的vector
ACE_SString 提供了简单的字串操作
ACE_WString 宽字符类
ACE_TString 字符类的typedef
ACE_Dirent_Selector 目录处理类
ACE_Base64 Base64编码类
ACE_Arg_Shifter 参数构造转换类
ACE_Atomic_Op 原子操作类
ACE_Auto_IncDec 自动增加计数器
ACE_Adaptive_Lock 提供统一的锁API
ACE_Auto_String_Free 简单的字符串指针释放类
ACE_Tokenizer 字串分割类
ACE_Time_Value 定时器使用方法
ACE_ARGV main参数处理类
ACE_ARGV cl (argv);
// My own stuff.
ACE_ARGV my;
// Add to my stuff.
my.add (ACE_TEXT ("-ORBEndpoint iiop://localhost:12345"));
// Print the contents of the combined
for (int i = 0; i < my.argc (); i++)
ACE_DEBUG ((LM_DEBUG,ACE_TEXT (" (%d) %s\n"),i,my.argv ()[i]));
ACE_Auto_Event 象WIN32的事件类
ACE_Event 事件类使用wait()阻塞方法和signal()发送信号方法
static void *
worker (void *)
{ACE_Event evt();
for (int iterations = 1;
iterations <= n_iterations;
iterations++)
{
ACE_Time_Value wait (0,
iterations * 1000 * 100); // Wait 'iter' msec
ACE_Time_Value tv = ACE_OS::gettimeofday () + wait;
if (evt.wait (&tv) == -1)
{
// verify that we have ETIME
ACE_ASSERT(ACE_OS::last_error() == ETIME);
++timeouts;
ACE_Time_Value diff = ACE_OS::gettimeofday ();
diff = diff - tv; // tv should have been reset to time acquired
long diff_msec = diff.msec ();
if (diff_msec > ACE_ALLOWED_SLACK)
{
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("Acquire fails time reset test\n")));
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("Diff btw now and returned time: %d ms\n"),
diff.msec ()));
test_result = 1;
}
// Hold the lock for a while.
ACE_OS::sleep (ACE_Time_Value (0,
(ACE_OS::rand () % 1000) * 1000));
evt.signal ();
}
ACE_Thread::yield ();
}
return 0;
}
ACE_Profile_Timer 性能定时器
ACE_Profile_Timer timer;
ACE_Profile_Timer::ACE_Elapsed_Time elapsed_time;
timer.start ();
timer.stop ();
timer.elapsed_time (elapsed_time);
ACE_High_Res_Timer 高精度定时器
ACE_High_Res_Timer timer;
timer.reset ();
timer.start ();
while (i--)
{
ptr = allocator.malloc (chunk_size);
allocator.free (ptr);
}
timer.stop ();
timer.elapsed_time (tc);
ACE_Date_Time 时间类
ACE_Date_Time dt;
long month = dt.month ();
long day = dt.day ();
long year = dt.year ();
long hour = dt.hour ();
long minute = dt.minute ();
long seconds = dt.second ();
long usec = dt.microsec ();