技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 系统架构 --> 让数据解析能够做到向前向后完全兼容(最近做项目总结)

让数据解析能够做到向前向后完全兼容(最近做项目总结)

浏览:1594次  出处信息

最近在做项目的时候,遇到一个问题,即结构体内的字段可能会在未来的时间内不停的增加(不会减少或者删除),所以在打包解包的时候就会涉及到版本兼容的问题,并且是向前和向后同时兼容。

我们先来看一下,如果结构体的内容永远不变,那么我们用结构体自解析的方法:

typedef struct _farmbase_land1
{/*{{{*/
    unsigned char ID;
    unsigned char bitmap;
    _farmbase_land1()
    {
        ID = 0;
        bitmap = 0;
    }
    int Output(unsigned int /*ver*/,char*& buff,int& iLen,int iMaxLen)
    {/*{{{*/
        int needLen = sizeof(unsigned char)*2;
        if(needLen>iMaxLen)
        {
            return FBErrSystemNoMem;
        }
        char *t_Buff = buff;
        *(unsigned char*)t_Buff = ID;
        t_Buff+=sizeof(unsigned char);
        *(unsigned char*)t_Buff = bitmap;
        t_Buff+=sizeof(unsigned char);
        iLen = t_Buff - buff;
        return 0;
    }/*}}}*/
    int Input(unsigned int /*ver*/,char *buff,int& iLen,int iMaxLen)
    {/*{{{*/
        int needLen = sizeof(unsigned char)*2;
        if(needLen>iMaxLen)
        {
            return FBErrSystemNoMem;
        }
        char *t_Buff = buff;
        ID = *(unsigned char*)t_Buff;
        t_Buff+=sizeof(unsigned char);
        bitmap = *(unsigned char*)t_Buff;
        t_Buff+=sizeof(unsigned char);
        iLen = t_Buff - buff;
        return 0;
    }/*}}}*/
}CFarmBaseLand1;/*}}}*/

可以看出,结构体能够自己在打包/解包的时候,返回使用了的buff的长度,所以,如果我们是处理上述结构体的一个数组,那么代码可以这样写:

static int Output(unsigned int ver,char *buff,int& iLen,int iMaxLen,map<unsigned int,T>* ptrMap)
{/*{{{*/
    if(ptrMap==NULL)
    {
        return -1;
    }
    char *t_Buff = buff;
    int t_Len=0;
    int t_MaxLen=iMaxLen;
    *(unsigned short*)t_Buff = ptrMap->size();
    t_Buff += sizeof(unsigned short);
    for(typename map<unsigned int,T>::iterator it=ptrMap->begin();it!=ptrMap->end();++it)
    {
        t_MaxLen = iMaxLen - (t_Buff-buff);
        int ret = it->second.Output(ver,t_Buff,t_Len,t_MaxLen);
        if(ret)
        {
            return -3;
        }
        t_Buff += t_Len;
    }    
    iLen = t_Buff - buff;
    return 0;
}/*}}}*/
static int Input(unsigned int ver,char *buff,int& iLen,int iMaxLen,map<unsigned int,T>* ptrMap)
{/*{{{*/
    if(ptrMap==NULL)
    {
        return -1;
    }
    if(iMaxLen == 0)
    {
        //这个字段暂时没有数据
        iLen = 0;
        return 100;
    }
    int t_Len=0;
    int t_MaxLen=iMaxLen;
    char *t_Buff = buff;
    if(sizeof(unsigned short)>(unsigned int)t_MaxLen)
    {
        return -2;
    }
    unsigned short sCount = *(unsigned short*)t_Buff;
    t_Buff += sizeof(unsigned short);
    for(unsigned int i = 0;i<sCount;++i)
    {
        T t_data;
        t_MaxLen = iMaxLen - (t_Buff - buff);
        int ret = t_data.Input(ver,t_Buff,t_Len,t_MaxLen);
        if(ret)
        {
            return -3;
        }
        t_Buff += t_Len;
        (*ptrMap)[t_data.ID] = t_data;
    }
    iLen = t_Buff - buff;
    return 0;    
}/*}}}*/

但是这样自解析带来的最大问题就是,我们会控制大量的版本,并且每次升级版本都要重发所有程序,这个成本是非常大的。

所以,我们需要对这种解析方式进行更改,需要考虑两个问题:
1.当新的API读到旧的格式的数据的时候,并写回的时候,怎么做。(即兼容旧数据)
2.当旧的已经发布的API读到新的数据的时候,并写回的时候,怎么做。(即兼容新数据)

答案是:
1.当新API读到旧的数据的时候,新API多的参数用默认值填充,写回的时候按照新API的格式写回。
2.当旧API读到新数据,自己不认识的那段buff,要保存起来,写回的时候,将这段buff原样memcpy。

所以output和input函数将会升级成这个样子:

typedef struct _farmbase_land1
{/*{{{*/
    unsigned short precId;
    string extrabuff;
    _farmbase_land1()
    {
        precId = 0;
    }
    int ExtraOutput(unsigned int /*ver*/,char*& buff,int& iLen,int iMaxLen)
    {/*{{{*/
        int needLen = extrabuff.size()+sizeof(unsigned short)+0+sizeof(unsigned char);//这个地方要加上最新的字段
        if(needLen>iMaxLen)
        {
            return FBErrSystemNoMem;
        }
        char *t_Buff = buff;
        *(unsigned char *)t_Buff = extrabuff.size()+sizeof(unsigned short)+0; //这里的0代表以后扩展字段的sizeof
        t_Buff+=sizeof(unsigned char);
        /*在这里添加字段*/
        *(unsigned short *)t_Buff = precId;
        t_Buff+=sizeof(unsigned short);
        if(extrabuff.size()>0)
        {
            memcpy(t_Buff,extrabuff.c_str(),extrabuff.size());
        }
        t_Buff+=extrabuff.size();
        iLen = t_Buff - buff;
        return 0;
    }/*}}}*/
    int ExtraInput(unsigned int /*ver*/,char *buff,int& iLen,int iMaxLen)
    {/*{{{*/
        char *t_Buff = buff;
        unsigned char t_size=0;
        unsigned char allsize = (unsigned char)*t_Buff;
        t_Buff+=sizeof(unsigned char);
        
/*
        //在这里可以任意的添加字段了
        //这里这样写,主要是为了当新的api读到老数据的时候
        unsigned char testdata;
        t_size = allsize - (t_Buff-buff-sizeof(unsigned char));
        if(t_size != 0)
        {
            testdata = (unsigned char)*t_Buff;
            t_Buff+=sizeof(unsigned char);
        }
        else
        {
            testdata = 0;
        }
        unsigned char testdata2;
        t_size = allsize - (t_Buff-buff-sizeof(unsigned char));
        if(t_size != 0)
        {
            testdata2 = (unsigned char)*t_Buff;
            t_Buff+=sizeof(unsigned char);
        }
        else
        {
            testdata2 = 0;
        }
        */

        t_size = allsize - (t_Buff-buff-sizeof(unsigned char));
        if(t_size != 0)
        {
            precId = *(unsigned short*)t_Buff;
            t_Buff+=sizeof(unsigned short);
        }
        else
        {
            precId = 0;
        }
        t_size = allsize - (t_Buff-buff-sizeof(unsigned char));
        extrabuff.resize(t_size);
        if(extrabuff.size()>0)
        {
            memcpy((char*)extrabuff.c_str(),t_Buff,extrabuff.size());
        }
        t_Buff+=extrabuff.size();
        iLen = t_Buff - buff;
        return 0;
    }/*}}}*/
}CFarmBaseLand1;/*}}}*/

而当解析上面的结构体数组时,则和原来的函数没有什么区别,所以保证了对外的接口统一。
最终问题完美解决~~

建议继续学习:

  1. 12款很棒的浏览器兼容性测试工具推荐    (阅读:4863)
  2. Javascript和CSS浏览器兼容总结    (阅读:3913)
  3. 如何更改字段至兼容的不同类型    (阅读:3082)
  4. 兼容所有浏览器的设为首页与显示小策略    (阅读:2955)
  5. 前端设计中的浏览器CSS Hack汇总    (阅读:2780)
  6. 定位元素间的Z值比较及z-index在不同浏览器下默认值的影响    (阅读:2668)
  7. 抛弃 CSS Hacks 后的浏览器兼容方案    (阅读:1966)
  8. 抛弃 CSS Hacks 后的浏览器兼容方案    (阅读:1686)
  9. js代码因逗号不规范导致IE不兼容的问题    (阅读:1143)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
  • 作者:Dante    来源: Vimer
  • 标签: 兼容
  • 发布时间:2010-03-11 23:37:52
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1