ACE技术论文集-第3章 高效可移植和灵活的网络编程的C++包装
发布: 2008-6-13 14:57 | 作者: Prashant Jain and D | 来源: 转载 | 查看: 597次
IPC SAP封装常用的基于句柄的IPC接口,比如socket、TLI、STREAM管道和FIFO。如图3-5所示,IPC SAP被设计为类属的一座“森林”,包括SOCK SAP(封装socket)、TLI SAP(封装TLI接口)、SPIPE SAP(封装UNIX SVR4 STREAM管道接口),以及FIFO SAP(封装UNIX FIFO接口)。

图3-5 IPC SAP类属关系
每个类属都被组织为继承层次。所有子类都给现有IPC机制的子集提供定义良好的接口。在一个层次中的所有子类共同地包含了一种特定通信抽象(比如Internet域或UNIX域的协议族)的全部功能。这一部分描述IPC SAP的设计目标,概述它的类属,并讨论在其OO设计之下的法则。
IPC SAP被设计用于改善通信软件的正确性、易学性和易用性、可移植性,以及可复用性,同时维持高水平的性能和功能。这一部分讨论IPC SAP是怎样实现这些目标的。
socket的若干问题都与它的弱类型检查有关。通过只允许对类的实例进行“类型安全”的操作,IPC SAP提高了网络应用代码的正确性。为强制实施类型安全性,IPC SAP确保它的所有对象都通过构造器来适当地初始化。此外,对IPC SAP对象只能进行良好定义的操作。
IPC SAP还被设计用于防止偶然的类型安全性违例。例如。SOCK SAP类属中的组件可防止偶然地对数据报对象进行面向连接的操作。因此,不可能在数据报对象上调用accept方法,在连接器和接受器工厂对象上接收(recv)或发送(send)数据,或是在面向连接的对象上调用sendto方法。
因为IPC SAP类是强类型的,任何执行非法操作的企图都会在编译时、而非运行时被拒绝。图3-14所示的buggy_echo_server的SOCK SAP修正版对这一点进行了演示。此例更正了图3-3中所标识出的所有socket问题。
简化常用IPC操作的使用是一个与正确性有关的目标。通过提供更简单的接口,开发者能够把注意力集中在编写应用上,而不是与低级网络代码搅在一起。一般而言,IPC SAP这样来简化它的网络编程接口:
提供辅助类,使应用与易错细节相屏蔽:例如,IPC SAP含有如图3-6所示的Addr类层次。该层次通过类型安全的C++接口来支持若干不同的网络寻址格式。Addr层次消除了若干常见的编程错误,这些错误都与直接使用基于C的struct sockaddr数据结构有关系。例如,不再有可能忘记把sockaddr地址结构清零。

图3-6 IPC SAP地址类层次
组合若干操作,以形成单一操作:例如,ACE_SOCK_Acceptor是用于被动连接建立的工厂。它的构造器执行创建被动模式服务器端点所需的多个socket系统调用(比如socket、bind和listen)。
为典型的方法参数值提供缺省参数:例如,accept的寻址参数常为NULL指针。为简化编程,这些值在SOCK_Acceptor::accept中作为C++缺省参数被给出,以使程序员不必显式地提供它们。
利用traits(特性)来传达“元类”信息:例如,所有IPC SAP类都含有一组统一的traits。这些traits进行类型定义,以指定与各自的IPC SAP类型相关联的地址类(例如,ACE_INET_Addr)和/或流类(例如,ACE_TLI_Stream)。如下所示:
class ACE_SOCK_Connector
{
public:
// Traits
typedef ACE_INET_Addr PEER_ADDR;
typedef ACE_SOCK_Stream PEER_STREAM;
// ...
};
class ACE_TLI_Connector : public ACE_SOCK
{
public:
// Traits
typedef ACE_INET_Addr PEER_ADDR;
typedef ACE_TLI_Stream PEER_STREAM;
//...
};
如3.7所示,traits与C++参数化类型的联合使用支持一种强大的称为“泛型编程”(generic programming)的设计范式 [18]。
在IPC SAP中使用了基于继承的层次分解,以增加多种IPC机制所共享的通用代码的数量。例如,IPC SAP给像fcntl和ioctl这样的较低级的OS设备控制系统调用提供了一种C++接口。通过在不同的子类间共享代码,继承增强了在IPC SAP实现中的复用。
例如,IPC SAP根基类提供的标准方法和数据被其他的派生类所共享。这些共享组件提供句柄和与其相关的set/get方法。此外,还提供了一些方法来在句柄上启用和禁止异步I/O、非阻塞I/O,以及紧急消息递送。
若干C++特性有助于增强IPC SAP的可移植性。例如,IPC SAP提供一种不依赖于平台的接口,通过使用C++模板来改善通信软件的可移植性。如图3-7所示,SOCK SAP和TLI SAP类的一个子集提供了同样的OO接口。每个平台可能会拥有不同的用于本地和远地网络编程(例如,socket vs. TLI)的底层接口。但是,有可能编写出应用,使用两个类中的任何一个来透明地进行参数化。这增强了应用的跨平台(这些平台可能不同时支持socket和TLI的平台)可移植性。
通过允许应用被它们所需IPC机制的类型参数化,类的使用(相对于独立的函数)有助于简化网络编程。如3.8所讨论的,参数化有助于改善平台间(这些平台支持不同的网络编程接口,比如socket或TLI)的可移植性。

图3-7 使用模板增强可移植性
为鼓励开发者用IPC SAP替换现有接口,IPC SAP被设计为能高效地运作。下列技术帮助改善了性能,而又没有牺牲清晰性和模块性:
使用内联函数:许多IPC SAP方法都被指定为C++内联函数,从而消除了调用IPC SAP方法的额外运行时开销。内联是一种合理的方法,因为每个方法都非常短(平均每个方法大约3行)。
避用虚函数:在IPC SAP继承层次中没有使用虚函数,从而改善了性能,因为(1)消除了间接的vtable函数指针分派,以及(2)便利了确实很短而又经常访问的方法(比如发送和接收用户数据)的直接内联。
这一部分描述组成IPC SAP的C++类属的OO设计,并特别强调了socket的SOCK SAP C++包装的设计。SOCK SAP已被移植到许多UNIX平台、以及WinSock网络编程接口上。对这一层面的细节不感兴趣的读者可能会想跳到3.8,在其中讨论的是SOCK SAP包装类的设计之下的一般法则。
SOCK SAP被设计用于克服3.4描述的socket的局限。使用C++包装来封装socket接口的主要好处是:
-增强类型安全性:SOCK SAP在编译时检测许多微妙的应用类型系统违例。
-可移植性:SOCK SAP提供了可移植的、平台无关的网络编程接口。
-易用性:SOCK SAP极大地减少了花费在较低级网络编程细节上的应用代码数量和开发工作。
-高效:SOCK SAP增强了上面所列的软件质量,而又没有牺牲性能[1]。
SOCK SAP类属为应用提供Internet域和UNIX域协议族[6]的OO接口。SOCK SAP由大约12个C++类组成。其总体结构对应于如图3-8所示的通信服务、连接角色和通信域的分类。将图3-4和图3-8进行比较富有启发意义。图3-8中的组件更为简洁,因为它们使用C++包装在依据继承关联的类中封装了多种socket机制的行为。

图3-8 SOCK SAP类和通信维度的分类

图3-9 SOCK SAP类属
SOCK SAP中的每个类都为组成全部类属的机制的一个子集提供一种抽象接口。多种类型的Internet域和UNIX域socket的功能是通过继承机制从下面描述的适当的类那里获得的。这些类以及它们的关系在图3-9中通过Booch表示法[19]显示。
应用通过继承或实例化图3-9中所示的适当的SOCK SAP子类来访问底层的Internet域或UNIX域socket类型的功能。如下所述,ACE_SOCK* 子类封装Internet域的功能,而ACE_LSOCK* 子类封装UNIX域的功能。
