这篇分析数据通过sproto协议的打包和解包.有了前面的铺垫,分析打包和解包较为得简单.
在实际传输数据的时候我们都是根据协议名传输数据, 即protocol数组中的某个元素.所以在打包时我们必须知道是在打包哪个协议,解包的时候就知道了对应的是哪条协议了, 这个信息要写入打包之后数据的头部.其实sproto协议中第一个名为package的sproto_type就是来描述这个信息的.
区分每条协议最好用id,而不是string名字,这样有利用协议更改.所以每个协议都是有一个tag字段来对应其名字.所以package的sproto_type字段类型为integer.
刚才说过打包一个协议首先打包头部,包含其协议的tag. 例如打包下面的协议:
sp.request("TestReq", "{"age":15,"sex":"abc"}", ...)
首先得找到"TestReq"协议的tag,打进包头里面.这个很容易,前面讲构造协议时,sproto结构体包含了协议所有信息.根据协议名TestReq遍历proto字段即可.包头根据sproto_type字段里的name和个数,遍历即可找到名"package"的sproto_type结构.
同理找到要打包的数据对应sproto_type类型结构也不难,他的名字为"TestReq.request".
重点就是如何把数据打包了.打包也就是要写入数据,依照前面讲的数据格式,除了小integer型,其他都比较复杂,但总体上是先确定数据区域的偏移,写入数据,然后写入数据大小.下面对应常用的数据类型分别来分析.
打包代码的原型是:
int sproto_encode(const struct sproto_type *st, void * buffer, int size, sproto_callback cb, void *ud);
其中st是sproto_type类型,buffer,size是要传出的缓存地址和大小. cb是打包每种数据时的回调函数,做成回调函数的目的是业务与逻辑分离,做到接口之外定制,也就是我们自己定义打包的方式. ud则是打包函数与回调函数交互的介质.
对于字符串类型和自定义类型,都是调用encode_object函数:
static int
encode_object(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int size) {
int sz;
if (size < SIZEOF_LENGTH)
return -1;
args->value = data + SIZEOF_LENGTH;
args->length = size - SIZEOF_LENGTH;
sz = cb(args);
if (sz < 0) {
if (sz == SPROTO_CB_NIL)
return 0;
return -1; // sz == SPROTO_CB_ERROR
}
assert(sz <= size - SIZEOF_LENGTH); // verify buffer overflow
return fill_size(data, sz);
}
可以看出他首先是确定要写入数据的地址,写完数据之后填入数据长度.要传入的数据和大小由args给出.
对于array数据,则多次循环调用打包数据,然后写入总长度,对于自定义数据则递归调用,详细的就不多说了.
解码则是打包的逆向过程,看看代码就能清楚了.
这篇写的比较粗糙,很多细节没有讲出来.其实只要对sproto协议做到心中有数,相信会理解.
参考:
https://www.cnblogs.com/RainRill/p/9002848.html