关于作者

姓名:潘凯

性别:男

出生日期:1975-01-31

地区:深圳

联系电话:

QQ:123283婚否:已婚
用户名:潘凯
笔名:潘凯
地区: 深圳
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



FORMS人员链接

朋友链接

访问统计:
文章个数:121
评论个数:183
留言条数:37




Powered by BlogDriver 2.1

潘凯的博客

 

道,深藏不露,难以理解。 (本处文章皆属原创,转载请注明出处,谢谢!)

文章

我的博客搬家了  (作者置顶)
我的博客搬家了,欢迎大家去做客。请记住这个永久域名,呵呵。

- 作者: 潘凯 2009年06月26日, 星期五 17:44  回复(0) |  引用(0) 加入博采

小照片  (作者置顶)


这几张都是2005年6、7月份照的相片

- 作者: 潘凯 2006年02月19日, 星期日 14:25  回复(8) |  引用(1) 加入博采

长大

  潘玥慢慢开始对时间,年龄有点概念了。有时候还会问我“爸爸,我小时候是怎么样子?”。
  我告诉她,“现在就是你的小时候。”

 
  有一次她悄悄的对我说:“爸爸,我不想长得太快了。小时候好玩一些。”
 
  一天,我躺着床上休息。潘玥爬上来,从后面用两个手勾着我的下巴用力的往后拔。
  “你要干什么,宝宝。”我不解地问她。
  “爸爸,我要把你拔高一些。”
  “爸爸现在就挺高的,干嘛还要把我拔高?”
  “我把你再拔高一些,这样等我长大了,你还是比我高,我还是小孩子。嘻嘻。”

- 作者: 潘凯 2009年06月13日, 星期六 10:08  回复(0) |  引用(0) 加入博采

长大

  潘玥慢慢开始对时间,年龄有点概念了。有时候还会问我“爸爸,我小时候是怎么样子?”。
  我告诉她,“现在就是你的小时候。”

 
  有一次她悄悄的对我说:“爸爸,我不想长得太快了。小时候好玩一些。”
 
  一天,我躺着床上休息。潘玥爬上来,从后面用两个手勾着我的下巴用力的往后拔。
  “你要干什么,宝宝。”我不解地问她。
  “爸爸,我要把你拔高一些。”
  “爸爸现在就挺高的,干嘛还要把我拔高?”
  “我把你再拔高一些,这样等我长大了,你还是比我高,我还是小孩子。嘻嘻。”

- 作者: 潘凯 2009年06月13日, 星期六 10:07  回复(0) |  引用(0) 加入博采

长大
  潘玥慢慢开始对时间,年龄有点概念了。有时候还会问我“爸爸,我小时候是怎么样子?”。
  我告诉她,“现在就是你的小时候。”
 
  有一次她悄悄的对我说:“爸爸,我不想长得太快了。小时候好玩一些。”
 
  一天,我躺着床上休息。潘玥爬上来,从后面用两个手勾着我的下巴用力的往后拔。
  “你要干什么,宝宝。”我不解地问她。
  “爸爸,我要把你拔高一些。”
  “爸爸现在就挺高的,干嘛还要把我拔高?”
  “我把你再拔高一些,这样等我长大了,你还是比我高,我还是小孩子。嘻嘻。”

- 作者: 潘凯 2009年06月13日, 星期六 10:05  回复(0) |  引用(0) 加入博采

长大
  潘玥慢慢开始对时间,年龄有点概念了。有时候还会问我“爸爸,我小时候是怎么样子?”。
  我告诉她,“现在就是你的小时候。”
 
  有一次她悄悄的对我说:“爸爸,我不想长得太快了。小时候好玩一些。”
 
  一天,我躺着床上休息。潘玥爬上来,从后面用两个手勾着我的下巴用力的往后拔。
  “你要干什么,宝宝。”我不解地问她。
  “爸爸,我要把你拔高一些。”
  “爸爸现在就挺高的,干嘛还要把我拔高?”
  “我把你再拔高一些,这样等我长大了,你还是比我高,我还是小孩子。嘻嘻。”

- 作者: 潘凯 2009年06月13日, 星期六 10:02  回复(0) |  引用(0) 加入博采

使用ACE_CDR类进行网络编解码(5-4)
  这次我们来处理边界调整的问题。留意下面的代码片段:
 
char buf[6] = {0};
ACE_OutputCDR ocdr(buf, 6, ACE_CDR::BYTE_ORDER_BIG_ENDIAN);
ACE_CDR::ULong temp1 = 88;
ACE_CDR::UShort temp2 = 66;
ocdr << temp1;
ocdr << temp2;
int result = peer.sendn(buf, 6);
 
  上面的代码将两个数据压到buf中。这里面有一个隐藏的BUG。第二行用一个buf来构造ACE_OutputCDR对象时,ACE_OutputCDRr构造函数会进行一个复杂的操作。它先用这个buf构造一个ACE_Message_Block,然后对这个ACE_Message_Block调用ACE_CDR::mb_align方法,进行一次边界调整。
  如果刚好buf的起点不在4字节的边界上(不能整除4),则会将ACE_Message_Block的起点后移到4字节对齐的边界上。这会造成两个可能的后果。如果调整的确发生了(假如往后调整了2个字节),那么上述代码最后一行发送的内容,实际上就是错误的,因为错开了2个字节。更为严重的错误是我们的buf刚好是6个字节,我们也写入了6个字节,但是如果ACE_OutputCDR替我们做了一次调整的话,在写入的时候就会越界,破坏堆栈(覆写buf数组后面的两个字节)。
  解决的方式有两个。如果是像上述的代码一样,用CDR类来对原始的buffer进行处理,那么可以通过在config.h文件中定义下例的宏来屏蔽对齐行为。
#define ACE_CDR_IGNORE_ALIGNMENT
  注意要重新编译ACE。
  另外一种方法是,如果是ACE在项目中用得比较普及的话,建议不要直接用原始buffer,改用ACE_Message_Block。实际上对齐时是调整了内部的ACE_Message_Block的base指针。如果总是通过ACE_Message_Block的base方法来得到实际buffer的起始,就不用担心会发生错位。这里唯一要注意的就是要为可能的调整留出空间,避免上面说的溢出。比如下面的代码:
ACE_Message_Block mb(1024 + ACE_CDR::MAX_ALIGNMENT);
  我们需要一个1024大小的buffer,但是在实际申请空间时加一个冗余值 ,对齐最大也不可能超过这个冗余,这样就避免了压入数据时引起越界。
  ACE进行一次对齐的原因是为了加快内存操作。结合前面“紧缩”部分的描述,我们可以知道,在缺省情况下,ACE在编解码时不但将Buffer的起始外进行对齐处理,里面的数据类型不论大小也是按4字节对齐的。有兴趣的可以看一下MAX_ALIGNMENT这个冗余量的值是8而不是4,我猜可能是为了兼容64位机器。
 
  (待续)

- 作者: 潘凯 2009年02月16日, 星期一 20:48  回复(0) |  引用(0) 加入博采

使用ACE_CDR类进行网络编解码(5-3)
  在缺省情况下,ACE_InputCDR和ACE_OutputCDR这两个类是不处理紧缩的,统一按4字节对齐。即小于4字节的数据类型压进去后都占4个字节。这样做主要是处于效率的考虑。32位的CPU在处理按4字节对齐的数据时最快,不需要再处理额外的偏移。但这样明显不符合我们前面描述的情况,我们不希望编码后的数据f中留有空洞。要解决这个问题我们需要在config.h文件中再添加一行:
#define ACE_LACKS_CDR_ALIGNMENT
  这样数据在编码时就会按实际的大小进行紧缩。注意修改了config.h文件后要重新编译ACE。
  我们可以看一下CNPv1第四章的例子代码,这里存在一个不够严谨的地方。日志消息的消息头由一个布尔变量指示所用的编码字节序,以及一个unsigned long指示后面消息体的长度。布尔变量虽然只占1字节,但是由于没有定义上述的宏,编码后也占用了4个。例子中的代码在编码日志消息时消息头和消息体是分别编码的,然后集中发送(使用了sendv_n)。
  这里的问题是在代码中写死了消息头的长度为8字节。在缺省情况下布尔和整型都是4字节,没有错。但是一旦定义了上述的宏以后,布尔变量在编码后只占1字节,整个消息头的有效部分就只有5字节了,后面跟着3个字节未定义,然后再是消息体。解码的代码是顺序依次往后解。在定义了上面宏的情况下,没有跳过那3个未定义的字节,因此解出来的消息体都错位了。大家可以在定义了上述宏,并重新编译了ACE后,跑一下那个例子就会发现解码时会出错。
  简单的解决方法就是在解码时,解完头后显式的跳过那三个字节。最正确的解决方法是不要假设数据在编码后占的大小,只管顺序的压,顺序的解。只要保证两端用的是同一套编译的ACE库就OK了。
 
  到此基本上就达到我们的要求了。但是还有一个值得注意的问题,就是ACE_OutputCDR类会对它使用的buffer进行一次边界调整。我们在后面再描述这个问题。
 
  (待续)

- 作者: 潘凯 2009年02月7日, 星期六 15:36  回复(0) |  引用(0) 加入博采

使用ACE_CDR类进行网络编解码(5-2)

  ACE中提供了一组CDR类,专用于编解码,但它并不是专门针对我们在前面述情况的编解码情况,所以还必须进行定制。我们下面也按照不同的方面来讨论如果定制及使用ACE的CDR类来做网络数据的编解码。

 
  ACE_InputCDR类负责解码,ACE_OutputCDR类负责编码。这两个类都提供流式操作,使用很方便。唯一不爽的是它不提供链式操作。因为每一个“<<”或“>>”操作符重载函数返回的都是一个布尔变量,不像标准库那样返回表达式左值的引用。
 
  默认情况这两个类不对字节序进行任何转换,也就是总是使用主机字节序。如果两个主机间的字节序不一样,直接使用这两个类在一个主机上编码,再在另一个主机上解码,就会导致出错。CNPv1第四章中的例子通过在每一个消息头前面增加一个布尔变量来指定编码时的字节序,以解决这个问题。编码时首先把本地的主机字节序用一个布尔变量压进去,解码时首先解出这个布尔量,再用这个布尔量来设置ACE_InputCDR的字节序。这样做又麻烦又容易出错。由其不便于异种系统之间的对接。最方便的方法也是通用的方法,就是编解码都使用网络字节序,虽然会因为不必要的转换带来一点效率损失,但处理简单,也便于扩展。
 
  要指定编解码都使用网络字节序,我们可以通过在创建这两个类的对象时进行显示的指定,或是调用reset_byte_order方法进行设置。以创建对象为例:
ACE_OutputCDR ocdr(&mb, ACE_CDR::BYTE_ORDER_BIG_ENDIAN);
ACE_InputCDR icdr(&mb, ACE_CDR::BYTE_ORDER_BIG_ENDIAN);
  对于ACE_OutputCDR类,第二个参数指定了要使用大端表示法对数据进行编码。对于ACE_InputCDR类,第二个参数指定了待解码的数据是用大端表示法编码的。
  要注意的是,光有这样的指定还不够。缺省用ACE_OutputCDR进行编码时,它并不对写入的数据进行大小端转换,即使做了上述的指定。
  解决方法是,在编译ACE时,在config.h文件中加下下面的行。
#define ACE_ENABLE_SWAP_ON_WRITE
  这样就可以了。CDR类会自动的处理是否需要进行转换。
  还有一点要注意的,BYTE_ORDER_BIG_ENDIAN这个枚举在较早的的ACE版本中没有。5.6.5及以前版本中是没有的,5.6.7中有,5.6.6我没有用过。如果你用的是较早版本,可以直接用硬编码的数字。
BYTE_ORDER_BIG_ENDIAN = 0;
BYTE_ORDER_LITTLE_ENDIAN = 1;
  这样我们就解决了字节序的问题。下面接着处理紧缩的问题。
 
  (待续)

- 作者: 潘凯 2009年02月2日, 星期一 20:37  回复(0) |  引用(0) 加入博采

使用ACE_CDR类进行网络编解码(5-1)
  在网络上传输数据,我们需要对数据进行编码和解码。网络传输的编解码通常要处理字节序和紧缩的问题。
 
  字节序就是超过一个字节的数据类型在内存中的表示方式。关于字节序的详细介绍请在网上搜索相应的文章。约定的网络字节序采用大端表示法,但是intel架构的PC机,包括现在很多intel芯片的服务器都是用小端表示法。早期的大型机和小型机多用大端表示法。
  现在来看,网络字节序采用大端表示法,其实是很不“环保”的。互联网中的绝大多数电脑是用小端表示法,可偏偏选用大端表示法作为网络字节序。这样导致了很多没必要的转换。如果网络字节序采用小端表示法,每天发生在互联网上的字节序转换会少得多,呵呵。
  除非你写的是一个封闭的系统,且你可以100%肯定系统内所有机器都使用相同的字节序,否则还是应该坚持使用网络字节序(也就是大端(big-endian)表示法)来进行网络数据的编解码。
  一般用htons,htonl进行本地字节序到网络字节序的转换,即(host to net),前者处理short即2字节整数,后者处理long即4字节整数。如果本地字节序也是大端表示法,则这两个方法什么也不做。在有的系统上这两个方法被实现成宏,以节约一次调用开销。于之对应的还有ntohs和ntohl,负责从网络字节序转换到本地字节序。
  对于long long类型即8字节整数,就只能自己进行转换,比较麻烦。
 
  紧缩一般是为了避免不同主机间字节对齐的标准不一样,同时还可以节约带宽。比如下面的结构体。

struct a
{
   char a_;
   long b_;
}

  在windows平台用vc编译,默认情况下这个结构体的实例的大小为8个字节,即按4字节进行了对齐。一般对于这样的数据在编码时会进行一次紧缩,紧缩后变为5个字节。解码时再对逐个成员赋值一次,恢复为本地标准。
 
  自行处理这些细节,并不复杂,但很繁琐。而且出了问题调试很麻烦,一般要直接看内存buffer才能定位问题。所以尽量不要自己干这个事。
 
    (待续)

- 作者: 潘凯 2009年01月21日, 星期三 18:06  回复(0) |  引用(0) 加入博采