我今天测试发现ACE_InputCDRACE_OutputCDR并不是那么简单。

如果稍微不注意就会出现一些奇怪的字节错位的情况。

在这里我总结一下使用的规则:

         ACE_OutputCDR out(mb);

         out  << ?? ;     //这种方法简称out

         例一:

         如果out 了一个shortout一个long

         字节将成为如下排列(16进制)            xx xx ?? ?? xx xx xx xx

         其中,问号部分即是因为在long写入时进行的按long长度对齐。该数据是无意义的

        

         例二:

         如果out了一个charout一个 short

         字节将成为如下排列            xx  ??  xx xx

         其中,问号部分即是因为在short写入时进行的按short长度对齐。该数据是无意义的

        

        

         例三:

         如果out了一个shortout一个char

         字节将成为如下排列            xx  xx    xx

         这里输入的char不会造成对齐问题,因为char只有一个字节(都是倍数)

        

         例四:

         如果out了一个shortout一个char out 一个 long

         字节将成为如下排列            xx  xx  xx  ??  xx  xx  xx  xx

        

         按照以上可总结出,每out一个类型的数据时,输出流将自动按照这个输入类型的长度序对齐,如果没有对齐,则自动填充。

     导致读数据时产生非常多的问题。(你不见CNPV1中都是强转成ACE_CDR::ULong吗?这下知道了吧)。

解决方案有两种:

方案一:全部一种类型吧!比如全部用ACE_CDR::ULong

    不过这种方法通常不适合实际的应用。让偶动物园墙垮来帮大家解决这个心结。:) 西西,请看下一方案。

方案二:

大家都会觉得这玩意被做出来怎么就不好用呢?没道理吧。我也觉得没道理,所以为了证实ACE开发者都是大牛,偶就把ACE的代码挖出来给大家看看,

看看这里到底有着什么样的玄机?

我们就拿ACE_ULong来开刀:

大家翻开CDR_Stream.inl文件,找到以下语句

ACE_INLINE ACE_CDR::Boolean
operator<< (ACE_OutputCDR &os, ACE_CDR::ULong x)
{
  os.write_ulong (x);
  return (ACE_CDR::Boolean) os.good_bit ();
}

继续下去 os.write_ulong (x);

ACE_INLINE ACE_CDR::Boolean
ACE_OutputCDR::write_ulong (ACE_CDR::ULong x)
{
  const void *temp = &x;
  return this->write_4 (reinterpret_cast<const ACE_CDR::ULong*> (temp));
}

继续跟踪this->write_4 (reinterpret_cast<const ACE_CDR::ULong*> (temp));

ACE_CDR::Boolean
ACE_OutputCDR::write_4 (const ACE_CDR::ULong *x)
{
  char *buf = 0;
  if (this->adjust (ACE_CDR::LONG_SIZE, buf) == 0)
    {

       ....

    }

}

再继续adjust (ACE_CDR::LONG_SIZE, buf)

ACE_INLINE int
ACE_OutputCDR::adjust (size_t size, char*& buf)
{
  return this->adjust (size, size, buf);
}

继续 this->adjust (size, size, buf);

ACE_INLINE int
ACE_OutputCDR::adjust (size_t size,
                       size_t align,
                       char*& buf)
{
  if (!this->current_is_writable_)
    return this->grow_and_adjust (size, align, buf);

#if !defined (ACE_LACKS_CDR_ALIGNMENT)
  const size_t offset =
    ACE_align_binary (this->current_alignment_, align)
    - this->current_alignment_;

  buf = this->current_->wr_ptr () + offset;
#else
  buf = this->current_->wr_ptr ();
#endif /* ACE_LACKS_CDR_ALIGNMENT */

  char *end = buf + size;

  if (end <= this->current_->end () &&
      end >= buf)
    {
#if !defined (ACE_LACKS_CDR_ALIGNMENT)
      this->current_alignment_ += offset + size;
#endif /* ACE_LACKS_CDR_ALIGNMENT */
      this->current_->wr_ptr (end);
      return 0;
    }

  return this->grow_and_adjust (size, align, buf);
}

好!我们找到了,原来校正的代码就在这里!仔细看看

#if !defined (ACE_LACKS_CDR_ALIGNMENT)

这里说明,如果你定义了ACE_LACKS_CDR_ALIGNMENT,那么ACE就不会帮你校准对齐字节啦!

哈哈,是不是很兴奋?别着急,让我们动手

打开config.h文件,加入以下一句

#define ACE_LACKS_CDR_ALIGNMENT

重新编译ACE,好,将新的lib加入工程

(如果你用了aced.dll或ace.dll,注意路径,或把新的dll拷贝到system32目录下)

让我们来运行一下原来的程序

         out << (ACE_CDR::UShort)1;

         out << (ACE_CDR::Long)2;

         ACE_LOG_MSG->log_hexdump( LM_DEBUG , mb->base() , out.length() , "t");

         字节将成为如下排列(16进制)            xx xx      xx xx xx xx

哈哈!你再试试以上四个例子,会发现都没有问题了!

那么,从现在开始,你还需要写自己的整编和解编的包装类吗?我看没有必要了吧。还不快动手修改你的工程?

补充:

如果不这么做。那么在接收端用ACE_IntputCDR进行 >> 操作时。如果没有进行读时的强制转换

比如 没有这样写

ACE_CDR::Char c;

ACE_CDR::UShort s;

ACE_InputCDR in(...);

in >> (ACE_CDR::ULong)c;

in >> (ACE_CDR::ULong)s;

而是这样写:

ACE_InputCDR in(...);

in >>  c;

in >> s;

必出各种字节乱码问题。因为中间多了CORBA的marshaling 与demarshaling对齐数据。

如果按照以上方法重新编译ACE后。即可省事多了(同时也节省了网络流量)

ACE_OutputCDR out(..);

out << c;

out << s;

------------------------------------

ACE_InputCDR in(...);

in >>  c;

in >> s;

无须任何类型转换。绝不出错。

再次声明。你不必自己写整编和解编。该论坛多次讨论过该问题。这是一个非常好的解决之道。

         out << (ACE_CDR::UShort)1;

         out << (ACE_CDR::Long)2;


         ACE_LOG_MSG->log_hexdump( LM_DEBUG , mb->base() , out.length() , "t");


 


 


 

书中第四章中有一段代码用到了ACE_CDR的"opertaor> > ()"提取操作符,对此有些疑问。

代码大致如下

C/C++ code
ACE_InputCDR cdr(payload); //payload 是一个ACE_Message_Block

ACE_CDR::Boolean byte_roder;
//字节顺序标志
//Use helper method to disambiguate booleams from chars
cdr >> ACE_InputCDR::to_boolean(Byte_order); //[color=#FF0000]puzzle1[/color]
cdr.reset_byte_order(byte_order); //[color=#FF0000]puzzle2[/color]

//提取payload的长度
ACE_CDR::Ulong length;
cdr
>> length; //[color=#FF0000]puzzle3

[/color]


对上面的几行代码主要有2个疑问。
问题一、puzzle1和puzzle3处
cdr   > >   ACE_InputCDR::to_boolean(Byte_order);    
cdr   > >   length;
这两个地方,cdr直接通过   > >   就提取出来了ACE_InputCDR::to_boolean(Byte_order)和length,请问这是怎么回事儿?
是这两个部分的内容是按某种规定好的顺序就保存在cdr中的么?还是说在operator> > ()重载时,就已经对> > 后面的变量类型做了多态,以致> > 后面的内容类型不同,就自动从cdr中提取出不同的信息作为输出?

问题二   puzzle和puzzle3处
这两句代码书中的解释是cdr提取出payload中的“字节序标志”(byte_order)。然后,根据输入中指定的字节顺序,确定CDR流的字节顺序,提取具有不定长度的日志记录payload的长度。

我的疑问是,既然cdr需要知道payloay的“字节顺序标志”(cdr.reset_byte_order(byte_order)),然后再提取 payload的长度(cdr   > >   length)。那么cdr   > >   ACE_InputCDR::to_boolean(byte_order)这句是从哪里提取出来的“字节顺序标志”,我怎么感觉就是从payloade 里提取出来的呢(ACE_InputCDR   cdr(payload))?

盼各位执教,十分感谢!!

自己看看ACE的源码不就很清楚了..我们接收到一个数据包之后(数据存放在ACE_Message_Block中),用ACE_InputCDR来对接收到数据进行解编(解析),
那个byte_order就是接收到的数据的字节序标记.然后通过cdr.reset_byte_order(byte_order)告诉 ACE_InputCDR,它内部的ACE_Message_Block保存的数据是啥字字节序..如果接收的数据的字节序和本地字节序不一样,那么 ACE_InputCDR解编的时候就会自动在内部进行字节序的转换
posted on 2008-03-19 15:44 活着就是折腾,所以当然要骠悍的折腾 阅读(632) 评论(0)  编辑 收藏 引用 所属分类: ACE

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理