ACE_Message_Block类

来源:互联网 发布:poi软件 编辑:程序博客网 时间:2024/05/17 06:14

该类的设计意图:
 网络应用程序大都需要一种方法来高效的处理消息。标准的消息管理操作包括:
 (1)收到来自“网络”或来自“同一主机上其他进程”的消息后,将这些消息保存到缓冲区中;
 (2)当消息经过用户洗衣栈时,添加或删除消息的头和尾。
 (3)将消息分段并重新组装,使其适合网络的最大传输单元(MTU)。
 (4)将消息保存到缓冲区,以进行传输,或再次传输。
 (5)对“未按顺序”接受到的消息做好记录。
 
 ACE_Message_Block类,可以高效的管理具有“固定”和“可变”长度的消息。
 ACE_Message_Block提供以下功能:
 (1)每一个ACE_Message_Block都包含一个指针,指向“带引用计数器”的ACE_Data_Block,而ACE_Data_Block指向“和消息管理”的实际数据。
   采用这一设计,我们可以灵活、高效的共享数据,并降低内存复制带来的额外开销。
 (2)允许多个消息连接在一起,形成一个单链表,从而支持复合消息。
 (3)允许将多条消息连接起来,形成一个双链表,构成ACE_Message_Queue类的基础。
 (4)将“同步”和“内存管理”特性作为“无需修改底层ACE_Message_Block实现,应用程序就可以改变”的特征来对待。
 
 简单消息:只包含一个ACE_Message_Block
 复合消息:包含多个ACE_Message_Block
 
 
ACE_Message_Block的主要方法:
 ACE_Message_Block()   |
 init()                               |对消息进行初始化
 msg_type()                     设置和获取消息的类型。
 msg_priority()                 设置和获取消息的优先级。
 clone()                            返回整条消息的完整“深复制”。
 duplicate()                      返回消息的“浅复制”,将其应用计数增1。
 release()                         将引用计数减一,如果计数降至0,则释放消息的资源。
 set_flags()                      将制定的数据位(bit)同一组已有的标志(flag)执行“按位与”操作,用以制定消息的语义(例如消息释放时是否删除缓冲区等等)
 clr_flags()                      清除指定的标志位。
 copy()                            从缓冲区中赋值N个字节到消息。
 rd_ptr()                         设置和获取读指针。
 wr_ptr()                        设置和获取写指针。
 cont()                            设置和获取消息中的“连接”字段,这个字段用于将复合消息连接在一起
 next()                           |
 prev()                           |设置和获取“指向ACE_Message_Queue中的双向消息链表”的指针。
 length()                        设置和获取消息的当前长度,即wr_ptr()-rd_ptr()。
 total_length()               获取消息的长度,包括所有被连接的消息快。
 size()                            设置和获取消息的总容量,包括分配在【rd_ptr().wr_ptr()】范围之前及之后的存储容量。
 
 注意:rd_ptr()和wr_ptr()分别指向的是数据存储空间中活动部分的头部和尾部。

 

ACE_Message_Block的使用:
创建消息块
释放消息块
从消息块中读写数据
数据的拷贝
其它常用函数

1。创建消息块

创建消息块的方式比较灵活,常用的有以下几种方式 :

(1)直接给消息块分配内存空间创建。

    ACE_Message_Block *mb = new ACE_Message_Block (30);

(2)共享底层数据块创建。

    char buffer[100];
    ACE_Message_Block *mb = new ACE_Message_Block (buffer,30);

这种方式共享底层的数据块,被创建的消息块并不拷贝该数据,也不假定自己拥有它的所有权。在消息块mb被销毁时,相关联的数据缓冲区data将不会被销毁。这是有意义的:消息块没有拷贝数据,因此内存也不是它分配的,这样它也不应该负责销毁它。

(3)通过duplicate()函数从已有的消息块中创建副本。

    ACE_Message_Block *mb = new ACE_Message_Block (30);
    ACE_Message_Block *mb2 = mb->duplicate();

这种方式下,mb2和mb共享同一数据空间,使用的是ACE_Message_Block的引用计数机制。它返回指向要被复制的消息块的指针,并在内部增加内部引用计数。

(4)通过clone()函数从已有的消息块中复制。

    ACE_Message_Block *mb = new ACE_Message_Block (30);
    ACE_Message_Block *mb2 = mb->clone();

clone()方法实际地创建整个消息块的新副本,包括它的数据块和附加部分;也就是说,这是一次"深拷贝"。

2。释放消息块

一旦使用完消息块,程序员可以调用它的release()方法来释放它。

如果消息数据内存是由该消息块分配的,调用release()方法就也会释放此内存。
如果消息块是引用计数的,release()就会减少计数,直到到达0为止;之后消息块和与它相关联的数据块才从内存中被移除。
如果消息块是通过共享已分配的底层数据块创建的,底层数据块不会被释放。
无论消息块是哪种方式创建的,只要在使用完后及时调用release()函数,就能确保相应的内存能正确的释放。

3。从消息块中读写数据

ACE_Message_Block提供了两个指针函数以供程序员进行读写操作,rd_ptr()指向可读的数据块地址,wr_ptr()指向可写的数据块地址,默认情况下都执行数据块的首地址。下面的例子简单了演示它的使用方法。

#include "ace/Message_Queue.h"
#include "ace/OS.h"

int main(int argc, char *argv[])
{
    ACE_Message_Block *mb = new ACE_Message_Block (30);
    ACE_OS::sprintf(mb->wr_ptr(),"%s","hello");
    ACE_OS::printf("%s/n",mb->rd_ptr ());
    mb->release();
    return 0;
}

注意:这两个指针所指向的位置并不会自动移动,在上面的例子中,函数执行完毕后,执行的位置仍然是最开始的0,而不是最新的可写位置5,程序员需要通过wr_ptr(5)函数手动移动写指针的位置。

4。数据的拷贝

一般的数据的拷贝可以通过函数来实现数据的拷贝,copy()还会保证wr_ptr()的更新,使其指向缓冲区的新末尾处。

下面的例子演示了copy()函数的用法。

    mb->copy("hello");
    mb->copy("123",4);

注意:由于c++是以'/0'作为字符串结束标志的,对于上面的例子,底层数据块中保存的是"hello/0123/0",而用ACE_OS::printf("%s/n",mb->rd_ptr ());打印出来的结果是"hello",使用copy函数进行字符串连接的时候需要注意。

5。其它常用函数

length()    返回当前的数据长度
next()    获取和设置下一个ACE_Message_Block的链接。(用来建立消息队列非常有用)
space()    获取剩余可用空间大小
size()    获取和设置数据存储空间大小。


补充:ACE_Message_Block的几个大小,让人头晕,下面这张图非常清晰地显示了它们的计算方法,如果使用wr_ptr往消息中写了数据,如果自己不挪动指针的话,length的大小是不会改变的,length的大小等于wr_ptr指针的位置减去rd_ptr的位置。

 
ACE_Message_Block使用示例:
 ACE_Message_Block* mb = new ACE_Message_Block(30);  //创建消息块

 ACE_DEBUG((LM_DEBUG, "capacity : %d, length : %d, space : %d/n", mb->capacity(), mb->length(), mb->space()));

 //获得写指针
 char* ptr = mb->wr_ptr();

 //将数据写入消息中
 ACE_OS::memcpy(ptr, "fzjfzjfzj", 3);

 //挪动写指针
 mb->wr_ptr(9);

 //测试下引用计数,duplicate是浅拷贝,这样mb2和mb就指向同一个消息了
 ACE_Message_Block *mb2 = mb->duplicate();

 //clone是深度拷贝,不会增加引用计数
 ACE_Message_Block *mb3 = mb->clone();

 //释放下试试
 //mb2->release();

 //将读指针往前挪动四个位置会导致length=5
 //mb->rd_ptr(4);

 ACE_DEBUG((LM_DEBUG, "capacity : %d, length : %d, space : %d, reference_count : %d/n", mb->capacity(), mb->length(), mb->space(),  mb->reference_count()));

 //再构造一个消息,用于和mb串联起来
 ACE_Message_Block* mb4 = new ACE_Message_Block(30);

 ptr = mb4->wr_ptr();

 ACE_OS::memcpy(ptr, "wxy", 3);

 //将mb和mb4串联起来
 mb->cont(mb4);

 //我期待这样导致循环链表,结果是程序卡死不动了,这里需要分析下cont的源代码
 //mb4->cont(mb);

 ACE_Message_Block* pMessageBlock = mb;

 //遍历消息
 
 /*for(; pMessageBlock != NULL; pMessageBlock = mb->cont())
 {
  //注意这个total_capacity的值哦
  ACE_DEBUG((LM_DEBUG, "data: %s, total_length: %d/n", pMessageBlock->rd_ptr(), pMessageBlock->total_capacity()));
  mb = pMessageBlock;
 }
 //注意:这个循环会形成死循环,mb->cont()的值就是mb4所指向的值,它不是空。
 */

 //在栈中构造一个消息,调用其release方法会导致断言失败
 //这是因为使用delete释放栈空间
 //ACE_Message_Block mb5 (30);

 //ptr = mb5.wr_ptr();

 //ACE_OS::memcpy(ptr, "wyy", 3);

 //mb5.release();

 

//使用copy的时候需要注意每次copy之后会在字符串后面添加一个'/0'

//下面的例子会存入"hello/0world"

ACE_Message_Block mb6 (30);

mb6.copy("hello");

mb6.copy("world");

//如果这样做的话就只会hello了

//mb6.copy("hello/0world");