网络服务器软件开发/中间件开发,关注ACE/ICE/boost

C++博客 首页 新随笔 联系 聚合 管理
  152 Posts :: 3 Stories :: 172 Comments :: 0 Trackbacks

 在进行 <<协议设计>>系列之前,先写点零碎的知识,做些铺垫.

 Google的libprotobuf,已经很强大了,开始接触的时候还是2.1版本,现在已经到了2.2了,新版最大的变化就是添加了libprotobuf-lite!,是libprotobuf的精简版,更加的轻量级,非常适合我的口味。
 我比较推崇这个数据交换引擎,平时也在自己的代码里面有过应用,比如做简单的IM,文件传输等,都极为方便,看重的几点好处如下(手册中描述的好处就省了):
       1.提供接口描述语言(IDL),无论是做客户端,还是做服务端,都在面向接口编程。而且IDL语法规则很简单,和C的结构体语法类似。
       2.自动生成序列化及反序列化代码,让开发人员脱离了协议的细节,把更多的精力放到业务逻辑的编写上

听起来不错,事实上也真不错,不过还有几个问题需要考虑:
    1.你的客户端能带上libprotobuf这个包吗?
    2.你的客户端如果只支持C语言,怎么办?比如手机客户端
    3.要读懂libprotobuf的编码原理,除了要有一定水平,还需要时间,其它项目成员能够接受吗?

     我身边有朋友就碰到一种情况:一个手游项目,客户端是C的,服务端c++的,而且客户端和服务端是异地开发,如果从头做起,需要先协商好每个消息的结构,开发时这里不可避免的要涉及消息的序列化及反序列化,如果挨个消息手动解析,工作量会很大,而且调试困难,容易出错,可见一个像libprotobuf这样提供IDL的工具是很有必要的。
    下面说一下,目前我的思路:
         1.libprotobuf是肯定不能用了
         2.为了便于双方理解,要更多的采用常规协议设计方法
         3.一个小巧的IDL还是需要的,只需要自动完成序列化和反序列化即可
    举个具体例子,假若有下面一个C的结构体:
       struct SLogin
        {
            uint32 nId_;//用户ID
            string sPwd_;//密码
            uint8  nStatus_;//登陆状态
        };
    这里为了说明方便,使用了std::string,可以用char数组代替,或者用<ptr,len>形式代替。那么编码的时候,常规方式就是uint32占用4个字节,uint8占用1个字节,都容易理解,这里不要用libprotobuf的varint。
    重点看一下string的编码方式,其中string可以是ASCII字符串,也可以是二进制的数据块。任何协议都遵循TLV结构,其中T为Type类型,L为Length长度,V为Value值,前面的uint32不用额外的TL是因为:(1)他是结构体的第一个成员,而第一个成员双方约定是整形,确定了T,(2)uint32本身说明了长度为4个字节。同样,由于双方约定第二个结构体成员是string类型,所以只需要确定长度即可。那么长度字段本身是固定长度,还是变长的呢?如果长度固定,最少需要2个字节,该情况下,对于很短的字符串也是2个字节的长度,有些浪费。所以最好采取变长的长度,varint,每个字节的低7位是有效承载,最高位表示后面是否还有高字节出现。
    另外,针对上面这样简单结构体,最好用python脚本写个小代码生成工具,自动生成序列化及反序列化的代码,应该不难。
    关于序列化,可以参考文本协议json做下对比,http://www.json.org/


posted on 2009-09-24 03:03 true 阅读(4625) 评论(1)  编辑 收藏 引用 所属分类: C++基础编码知识

Feedback

# re: C++结构体序列化的一点思考 2009-09-24 12:17 陈梓瀚(vczh)
以前尝试做过一个,标记了你需要的所有类,它们的父类,他们的成员和函数之后,就算给一个base指针,也能判断出是什么子类,然后正确序列化。当然这个一点都不轻量级,很重。  回复  更多评论
  


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