这篇试着对着代码分析一下sproto协议.
一个典型的sproto协议长如下的样子:
.package {
type 0 : integer
session 1 : integer
}
.Info {
money 0 : integer
sex 1 : integer
}
TestReq 1 {
request {
addr 0: *string
info 1: Info
age 2: *integer
nClubID 3: integer
name 4: string
}
response {
addr 0: integer
}
}
根据代码的定义,一个完整的协议结构体sproto主要包括sproto_type数组和protocol数组,以及他们各自的个数,定义如下:
struct sproto {
struct pool memory;
int type_n;
int protocol_n;
struct sproto_type * type;
struct protocol * proto;
};
拿上面的协议文件举例,protocol只有一个TestReq,而sproto_type有四个,分别是:
package,Info,TestReq.request,TestReq.response. 注意看最后两个,他们是在TestReq协议中定义的.
结构体protocol的定义如下:
struct protocol {
const char *name; //协议名字,如"TestReq"
int tag; //协议tag,上面是1
int confirm; // confirm == 1 where response nil
struct sproto_type * p[2]; //对应TestReq.request和TestReq.response
};
各字段的含义已经在注释里.
sproto_type的定义如下:
struct sproto_type {
const char * name; //名字,如"package"
int n; //field的个数,如Info有2个,TestReq.request有5个
int base;
int maxn;
struct field *f; //字段结构体
};
field就是来定义要交换的数据结构的,定义如下:
struct field {
int tag; //如addr为0
int type; //数据类型0为integer 2为数组 字符串 3为复合结构
const char * name; //如"addr"
struct sproto_type * st; //复合结构指向的sproto_type,内置类型为NULL
int key; //为map定义的key索引
int extra;
};
协议文件经打包后生成二进制数据,经解析之后对应一个sproto数据结构.一个简单的协议二进制数据如下:
可以看出数据主要分成了sproto_type和protocol两部分,图中的head,f1,f2字段和上篇中介绍的格式一致.
sproto_type或protocol数组的个数是由字段大小来计算得出的.
例如,spoto_type数组的总长度为0x93个字节.第一个元素的大小为0x47个字节,再往后读0x47个字节占用的大小为0x47+4小于0x93,故后面还有该类数据,继续读4字节为0x44,此时正好占用的数据为0x47+4+0x44+4=0x93,所以sproto_type数据读取完毕.
后面的protocol占0x17个字节,紧接着读取到第一个数据占0x13个字节,正好用完0x17个字节,所以protocol只有一个元素.
我们再来分析一下sproto_type数据的构成,如下图
后面0x2a个字节才是真正标识field数据的地方.例如各种不同的类型就是在type这个字段里表现的.
接下来分析protocol的结构:
好了,协议二进制文件格式就都讲到了,后面会讲如何实现打包解包.