peakzhang 发表于 2007-12-26 22:27:51

关于ACE_InputCDR和ACE_OutputCDR读写数据时的经验[原创*必读!]

by 动物园墙垮
我今天测试发现ACE_InputCDR和ACE_OutputCDR并不是那么简单。
如果稍微不注意就会出现一些奇怪的字节错位的情况。

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

         ACE_OutputCDR out(mb);
         out<< ?? ;   //这种方法简称out
         例一:
         如果out 了一个short再out一个long
         out << (ACE_CDR::UShort)1;
         out << (ACE_CDR:ong)2;
         ACE_LOG_MSG->log_hexdump( LM_DEBUG , mb->base() , out.length() , "t");

         字节将成为如下排列(16进制)            xx xx ?? ?? xx xx xx xx
         其中,问号部分即是因为在long写入时进行的按long长度对齐。该数据是无意义的
         
         例二:
         如果out了一个char再out一个 short
         字节将成为如下排列            xx??xx xx
         其中,问号部分即是因为在short写入时进行的按short长度对齐。该数据是无意义的
         
         
         例三:
         如果out了一个short再out一个char
         字节将成为如下排列            xxxx    xx
         这里输入的char不会造成对齐问题,因为char只有一个字节(都是倍数)
         
         例四:
         如果out了一个short再out一个char 再out 一个 long
         字节将成为如下排列            xxxxxx??xxxxxxxx
         
         按照以上可总结出,每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:ONG_SIZE, buf) == 0)
    {
       ....
    }
}

再继续adjust (ACE_CDR:ONG_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:ong)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;
无须任何类型转换。绝不出错。
再次声明。你不必自己写整编和解编。该论坛多次讨论过该问题。这是一个非常好的解决之道。



[ 本帖最后由 peakzhang 于 2007-12-26 22:28 编辑 ]

peakzhang 发表于 2007-12-26 22:30:46

疑问1:

(你不见CNPV1中都是强转成ACE_CDR::ULong吗?这下知道了吧)

这个我没有找到,是否所有的都这样转了?

疑问2:

比如 没有这样写

ACE_CDR::Char c;

ACE_CDR::UShort s;

ACE_InputCDR in(...);

in >> (ACE_CDR::ULong)c;

in >> (ACE_CDR::ULong)s;

这样的写法是否有点太强求了,如果你是按照Char和UShort整编的,那只需要使用

in >>c;

in >> s;

这样的写法就能真确解编。

疑问3:

ACE_LACKS_CDR_ALIGNMENT是用来进行决定是否要对齐操作的,你把他屏蔽了,是否会造成其他后果,比方说网络字节对齐等。

peakzhang 发表于 2007-12-26 22:30:54

加上ACE_LACKS_CDR_ALIGNMENT宏,应该会影响CORBA,因为默认要8字节对齐。但我们如果不用CORBA,用此宏无妨。
我自己试验过,如果没有此宏,解码的确会出现对齐的错误。

peakzhang 发表于 2007-12-26 22:31:02

我的实际测试代码:

ACE_Message_Block out_mb(1024);

ACE_OutputCDR out(&out_mb);

out<<(ACE_CDR::UShort)<<1;

out<<(ACE_CDR:ong)<<2;

out<<(ACE_CDR::Char)<<3;

out<<(ACE_CDR::UShort)<<4;

out<<(ACE_CDR::UShort)<<5;

out<<(ACE_CDR::Char)<<6;

out<<(ACE_CDR::UShort)<<7;

out<<(ACE_CDR::Char)<<8;

out<<(ACE_CDR:ong)<<9;

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

ACE_InputCDR in(out);

ACE_CDR::UShort a;

ACE_CDR:ong b;

ACE_CDR::Char c;

ACE_CDR::UShort d;

ACE_CDR::UShort e;

ACE_CDR::Char f;

ACE_CDR::UShort g;

ACE_CDR::Char h;

ACE_CDR:ong i;

in >> a;

in >> b;

in >> c;

in >> d;

in >> e;

in >> f;

in >> g;

in >> h;

in >> i;
测试结果:

1.能正确解编

2.

t-HEXDUMP 24 bytes

01 00 cd cd 02 00 00 00 03 cd 04 00 05 00 06 cd

07 00 08 cd 09 00 00 00

peakzhang 发表于 2007-12-26 22:31:35

我就是说的不用CORBA的情况。ACE_LACKS_CDR_ALIGNMENT这个宏也就是为不用CORBA字节对齐时使用的

peakzhang 发表于 2007-12-26 22:31:44

回sim。

并不是所有应用都需要对齐字节,不对齐字节并不表示容易出现解码错误。用了该宏,那么在收发两边按照同样的顺序读出,也不需要增加强制类型转换也不会出错。

根据不同的应用选择不同方法
页: [1]
查看完整版本: 关于ACE_InputCDR和ACE_OutputCDR读写数据时的经验[原创*必读!]