ACE: Adaptive Communication Environment 自适应通信环境,属于主机基础设施中间件
SAP:服务点访问
ACE模拟提供了所以C++标准库的类
下载地址:http://ace.ece.uci.edu/ 或 http://www.riverace.com
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-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的指针的静态