ACE_InputCDR和ACE_OutputCDR

来源:互联网 发布:知乎怎么发文章 编辑:程序博客网 时间:2024/05/18 01:04
 
这两个类的使用远没有书上介绍的那么简单,因为它们使用到一些非常细微的细节处理,比如对于字节序的处理、对于内存对齐的处理,以及对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得我们手动向后移。

原创粉丝点击