Chinaunix首页 | 论坛 | 博客
  • 博客访问: 37316
  • 博文数量: 20
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 250
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-29 17:42
文章分类

全部博文(20)

文章存档

2011年(1)

2010年(3)

2009年(16)

我的朋友

分类: 系统运维

2009-09-14 10:30:27

文章来源:http://blog.csdn.net/xjtuse_mal/archive/2009/01/07/3727234.aspx
这两个类的使用远没有书上介绍的那么简单,因为它们使用到一些非常细微的细节处理,比如对于字节序的处理、对于内存对齐的处理,以及对ACE_Message_Block的处理等。

1、字节序:缺省情况下,这两个类都使用了一种与CORBA兼容的方法处理字节序:发送端(将整型序列化成网络 字节串的一端)使用output_cdr << integer以本机字节序发送数据,而接收端(将网络字节串还原成整型的一端)需要判断一下送过来的字节序,先调用 input_cdr.reset_byte_order,然后再继续input_cdr >> integer。也就是说,output_cdr的字节序是无法改变的,它总是以CDR_STREAM_BYTE_ORDER宏定义的方式发送数据。
而 这一情况可以改变。若编译ACE时,我们预定义了ACE_ENABLE_SWAP_ON_WRITE宏,则生成的dll代码中就允许在 output_cdr上调用reset_byte_order。这样,我们可以以任意的字节顺序向Remote机器发送字节串。在某些情况下,这是一种解 决问题的方法。比如:服务器很快,而客户机很烂时,可以考虑由服务器代替客户机处理字节序问题,而客户机只管收就行了。

2、内存对齐。在从ACE_InputCDR获得数据、向 ACE_OutputCDR输出数据时,若取消了宏ACE_LACKS_CDR_ALIGNMENT宏的定义,则在操作2、4、8、16位数据类型前(如 Long),则ACE总会调用ACE_OutputCDR::adjust (size_t size,size_t align,char*& buf);或ACE_InputCDR::adjust (size_t size, size_t align, char*& buf);方法将数据对齐(分散)到align位置上,如4字节。这有利于提高数据访问速度,但是也带来一个问题。如:当我们使用 ACE_InputCDR执行以下代码时:
 
 cdr >> headerLength; //2字节
 
 cdr >> type;    //4字节
 
 cdr >> subType;   //4字节
 
 cdr >> *(ACE_CDR::LongDouble*)&sessionID(); //16字节
 
 cdr >> dataLength;  //4字节
 
 cdr >> dataStart;  //4字节
会莫名其妙地发现,实际ACE_InputCDR的rd_ptr向前走了不止上述字节数之和,而总是更多。同样,当使用ACE_OutputCDR向ACE_Message_Block写数据时,问题更为严重。我们执行完
 
 cdr << headerLength; //2字节
 
 cdr << type;    //4字节
 
 cdr << subType;   //4字节
 
 cdr << *(ACE_CDR::LongDouble*)&sessionID(); //16字节
 
 cdr << dataLength;  //4字节
 
 cdr << dataStart;  //4字节
若想当然地执行
 
 mb.wr_ptr(2+4+4+16+4+4);// ACE_Message_Block当前写指针后移
结果几乎总是错的。这是因为ACE_OutputCDR实际写的字节数几乎总是比2+4+4+16+4+4多。我们若在上述语句后继续追加数据到ACE_Message_Block,则刚才写入的一部分数据就会被冲掉。这仍是因为自动字节对齐的原因。
当然,ACE_InputCDR和ACE_OutputCDR以同样的规则进行对齐,它们之间是一致的,关键是它们和ACE_Message_Block之间的配合容易出问题。
 
一般来讲,我们可以使用以下方法:
 
//使用ACE_OutputCDR
 
size_t saveto(ACE_OutputCDR &cdr)
 
{
 
   size_t length1 = cdr.length();
 
   //输出任意长数据到cdr
 
   size_t length2 = cdr.length();  
 
   return length2-length1;
 
}
 
//使用ACE_InputCDR
 
size_t readfrom(ACE_InputCDR& cdr) 
 

 
   size_t length1 = cdr.length();
 
   //从cdr读任意长的数据
 
   size_t length2 = cdr.length();   
 
   return length1-length2;
 
}
然后,我们根据返回值再对ACE_Message_Block的rd_ptr和wr_ptr进行调整。

需要注意的是,虽然ACE_InputCDR和ACE_OutputCDR可以这样创建:
ACE_InputCDR cdr(mb);//mb是一个ACE_Message_Block
ACE_OutputCDR cdr(mb);//mb是一个ACE_Message_Block
但是,当使用cdr向mb输入、输出数据后,mb的rd_ptr和wr_ptr指针并没有动!需要我们手工调整。

3、ACE_InputCDR cdr(mb);都干了些什么!

若你拥有一个复合消息,由mb指向头部,大致是这种情形:
 
  mb-->[16字节长]-->[16字节长]-->[10字节长]
 
         消息块       消息块       消息块
那么,你执行下述语句
 
   ACE_InputCDR cdr(mb);
就会发现,cdr的 rd_ptr = = 0,(很正常啊),而cdr的wr_ptr = = 42。(没错是三个总和)。注意是ACE_InputCDR,因此,其wr_ptr仅仅起到边界作用,是无法向其追加数据的。
 
我们察看ACE_InputCDR cdr(mb),即构造函数,会发现它调用了以下语句:
 
     this->reset (mb, byte_order);
而reset调用了语句:
 
     this->reset_byte_order (byte_order);
 
     ACE_CDR::consolidate (&this->start_, mb);
其中this->start_是ACE_InputCDR内部的一个 ACE_Data_Block对象的引用。而consolidate调用了以下语句:
 
  for (const ACE_Message_Block* i = mb;
 
          i != 0;
 
          i = i->cont ())
 
   {
 
     (&this->start_)->copy (i->rd_ptr (), i->length ());
 
   }
也就是说,cdr内部的start_消息块的内容成了:
 
         &start_ --> [16字节长][16字节长][10字节长]
因此cdr的rd_ptr = = 0,而wr_ptr = = 42。就正常了。同时,我们还得到一条信息,就是对cdr的读取操作是跟原来mb没什么关系的。mb的rd_ptr从始致终没动地方。
 

 
相比之下,ACE_OutputCDR cdr(mb);简单一点。它在构造函数中调用了以下语句:
ACE_OutputCDR::ACE_OutputCDR (ACE_Message_Block *mb,
 
                             int byte_order,
 
                             size_t memcpy_tradeoff,
 
                             ACE_CDR::Octet major_version,
 
                             ACE_CDR::Octet minor_version)
 
start_ (mb->data_block ()->duplicate ()),
 
 ,……//其他初始化
 
{
 
   ACE_CDR::mb_align (&this->start_);
 
   this->current_ = &this->start_;
 
}
也 就是说,ACE_OutputCDR 对象cdr的start_直接引用了mb的ACE_Data_Block。当我们向对象cdr输出字节串时实际直接写到了mb的数据区中。不过有件事情得 我们自己做:cdr写数据时,只更新自己对象的wr_ptr,而mb对象的wr_ptr得我们手动向后移。

阅读(1105) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~