手机探索者开发实录—数据解包

手机探索者开发实录—数据解包

转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>

数据打包比较容易,解包却要困难得多。XML解析有DOM和SAX两种方式,我比较喜欢SAX方式,一是比较简单,不需要熟悉复杂的DOM API。二是可以边解析边处理。三是开源的expat简单易用,而且支持UTF-8编码。所以在手机探索者中,我们理所当然的采用SAX方式了。SAX是典型的builder模式,有了expat的帮助,我们要做的就是实现一些builder。

为了提高灵活性,也可以说是自找麻烦,我们允许同一个数据包中封装多个请求/响应/事件,不同请求的参数不同,所以build方式也是不同的,如果把这些请求的build放在一起,代码会显得到复杂。怎么办呢,在这里,我第一次发现状态模式有了用武之地,不同的请求不就相当于不同的状态吗,每解析到一个请求,我们就把当前builder切换到相应的builder上,这就相当于状态切换。可以建立一个查找表,根据名称找到相应的builder,这样逻辑上很清晰。

每个builder的实现很简单,毕竟里面的参数不多,不过要去写几十个builder很难说是件有趣的事。我喜欢单调的工作,因为我知道单调的工作总是有规律可循,找到这些规律就找到了解决问题的捷径。做了简单的分析之后,我发现参数的类型主要有三种:一是基本类型,像整数和字符串,二是结构,三是数组。其它所有类型都可以用这三种组合起来,于是就写了三个builder分别处理这三种类型。然后只要几行代码就可以为一个请求或者响应组合成一个builder。

Builder的接口定义如下,基本上是expat需要的接口:

  1. struct _MobileExplorerBuilder;
  2. typedef struct _MobileExplorerBuilder MobileExplorerBuilder;
  3. typedef MeRet (*MobileExplorerBuilderSetContextFunc)(MobileExplorerBuilder* thiz, void* ctx);
  4. typedef MeRet (*MobileExplorerBuilderOnStartFunc)(MobileExplorerBuilder* thiz, const char* name, con
  5. st char** atts);
  6. typedef MeRet (*MobileExplorerBuilderOnTextFunc)(MobileExplorerBuilder* thiz, const char* text, size
  7. _t length);
  8. typedef MeRet (*MobileExplorerBuilderOnEndFunc)(MobileExplorerBuilder* thiz, const char* name);
  9. typedef MeRet (*MobileExplorerBuilderDestroyFunc)(MobileExplorerBuilder* thiz);
  10. struct _MobileExplorerBuilder
  11. {
  12.     MobileExplorerBuilderSetContextFunc set_context;
  13.     MobileExplorerBuilderOnStartFunc on_start;
  14.     MobileExplorerBuilderOnTextFunc on_text;
  15.     MobileExplorerBuilderOnEndFunc on_end;
  16.     MobileExplorerBuilderDestroyFunc destroy;
  17.     char priv[0];
  18. };


虽然我们确定了用expat,它是开源的,跨平台的,也很好用。不过我还是不喜欢把自己绑定特定的函数库上,我决定它写一个parser函数,它只是简单的包装expat,使用起来更简单,也隔离了expat。

~~end~~

 
原文地址:https://www.cnblogs.com/zhangyunlin/p/6167625.html