ACE_Message_Block是ACE中消息传输的基本数据结构,声明在"ace/Message_Block.h"中。ACE_Message_Block看似只提供了操作单个数据缓冲区的接口,其实其实现比较复杂,使用使也必须了解其限制。 1、数据结构 首先,多个ACE_Message_Block可以按照两种方式组成链表,类似于4.4BSD-lite实现网络协议栈中的mbuf结构。 1) 使用next和prev方法提供的双向链表,常用于构建多个消息组成的消息队列。 2)使用cont方法提供的单向链表,用于扩展一个消息块的容量。并且,与获取单个消息块信息相对应,还提供了一些方便获取整个消息块链信息的接口。例如,与size对应的total_size;与capacity对应的total_capacity等。 通过上述两种链表,使得ACE_Message_Block可以同时在“横向“和”纵向“扩展。 2、对缓冲区的封装 对于原始字节缓冲区的封装,ACE_Message_Block分为两个层次来抽象和实现,即ACE_Message_Block和ACE_Data_Block。 ACE_Data_Block封装了原始字节缓冲区的操作,主要是对原始缓冲区的引用计数、深拷贝/浅拷贝、共享数据锁策略等。 ACE_Message_Block封装了对ACE_Data_Block的操作,主要是对ACE_Data_Block的引用计数、深拷贝/浅拷贝、共享数据锁策略等。此外,ACE_Message_Block还提供前面说到的两种链接机制。 3、内存管理 由于要用于各种场景,ACE_Message_Block的内存管理比较复杂。包括:两种管理策略,即引用计数和非引用计数方式;三种分配器,在ACE_Message_Block、ACE_Data_Block和原始字节缓冲区三个层面上,可以分别指定不同的内存分配器;不同的共享锁策略;不同的缓冲区来源,可以直接用内置分配器分配,也可以用外部指定缓冲区来初始化。下面是一些例子。 引用计数 非引用计数 -------------------------- -------------------------------- 构造 使用分配器分配内存 传入外部缓冲区指针 复制 duplicate copy 释放 release delete(或相应的内存回收函数) 由于ACE_Message_Block在一个类的接口中提供了如此多的使用方式,使得各种用法间很容易混淆,进而容易造成误用。因此,一般推荐的用法是,分配时通过内存分配器,采用动态内存分配的方式(而非之间创建在栈上),复制时使用引用计数的方式。 4、与ACD_CDR配合使用 ACE_CDR_Input和ACE_CDR_Output用于对字节解编和整编,它要求被解编的数据按8字节边界对齐。但ACE_Message_Block不提供任何字节对齐保证,所以与ACE_CDR_*共同使用时,应该使用使用ACE_CDR::mb_align()强制对齐。
虽然ACE自称是为了解决平台和语言中的“偶发复杂性”问题,但经过使用和阅读代码,才发现ACE_CDR_Input和ACE_CDR_Output 其实引入了不少偶发复杂性,使得编程很容易出错。例如:
1)ACE_CDR_Output构造时会在内部调用ACE_CDR::mb_align(x)方法进行字节对齐,但ACE_Input_CDR构造前要求程序员自己对齐。
2)ACE_CDR_Output支持ACE_Message_Block链,但ACE_Input_CDR不支持。
3)从ACE_Message_Block构造ACE_CDR_Output时,不会复制rd_ptr和wr_ptr位置;但ACE_Input_CDR会这么做。
4)从ACE_Message_Block构造ACE_CDR_*时,会深拷贝;从堆上的ACE_Message_Bloc构造ACE_CDR_*时,只会浅拷贝。
总之,ACE_CDR_*实现的不太好,是个充满陷阱的机制。