请选择 进入手机版 | 继续访问电脑版

skynet源码分析之sproto解析和构建 ,让你从繁琐中解脱

[复制链接]
林雨宣 发表于 2020-12-31 18:57:53 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
skynet提供一套与客户端通讯的协议sproto,设计简单,有利于lua使用,参考官方wiki https://github.com/cloudwu/skynet/wiki/Sproto。本篇先容组装".sproto"文件以及sproto构建流程。之后,会另写一篇先容sproto的使用方法。
1. 组装.sproto文件流程

以下面简单的test.sproto文件为例先容.sproto文件组装流程:
  1. -- test.sproto.Person {    name 0 : string    id 1 : integer    email 2 : string    .PhoneNumber {        number 0 : string        type 1 : integer    }    phone 3 : *PhoneNumber}.AddresBook {    person 0 : *Person}proto1 1001 {    request {        p 0 : integer    }    response {        ret 0 : *Person    }}
复制代码
通过sparser.parse api组装.sproto文件,参数text即test.sproto文件的内容:
  1. -- lualib/sprotoparser.lua function sparser.parse(text, name)     local r = parser(text, name or "=text")     dump(r)     local data = encodeall(r)     sparser.dump(data)     return data end
复制代码
第3-4行,通过lpeg库将.sproto文件分析转化成一个lua表,部分效果如下,包罗protocol和type两大类。protocol里包罗所有的协议,每个协议有request,response,tag三个key;type里包罗所有的范例,每个范例有1个或多个域(field),每个field里包罗name,tag,typename等信息。
  1.   "protocol" = {           "proto1" = {               "request"  = "proto1.request"               "response" = "proto1.response"               "tag"      = 1001           }       }  "type" = {           "AddresBook" = {              1 = {                  "array"    = true                  "name"     = "person"                  "tag"      = 0                  "typename" = "Person"              }          }          "Person" = {              1 = {                  "name"     = "name"                  "tag"      = 0                  "typename" = "string"              } ...
复制代码
第5-6行,把lua表按特定格式组装成二进制数据后,效果如下(每一行16个字节):
 
  1.   02 00 00 00 00 00 65 01 - 00 00 32 00 00 00 02 00   00 00 00 00 0A 00 00 00 - 41 64 64 72 65 73 42 6F   6F 6B 1A 00 00 00 16 00 - 00 00 05 00 00 00 01 00   04 00 02 00 04 00 06 00 - 00 00 70 65 72 73 6F 6E   6E 00 00 00 02 00 00 00 - 00 00 06 00 00 00 50 65   72 73 6F 6E 5A 00 00 00 - 12 00 00 00 04 00 00 00   06 00 01 00 02 00 04 00 - 00 00 6E 61 6D 65 10 00   00 00 04 00 00 00 02 00 - 01 00 04 00 02 00 00 00   69 64 13 00 00 00 04 00 - 00 00 06 00 01 00 06 00   05 00 00 00 65 6D 61 69 - 6C 15 00 00 00 05 00 00   00 01 00 06 00 08 00 04 - 00 05 00 00 00 70 68 6F   6E 65 4E 00 00 00 02 00 - 00 00 00 00 12 00 00 00   50 65 72 73 6F 6E 2E 50 - 68 6F 6E 65 4E 75 6D 62   65 72 2E 00 00 00 14 00 - 00 00 04 00 00 00 06 00   01 00 02 00 06 00 00 00 - 6E 75 6D 62 65 72 12 00   00 00 04 00 00 00 02 00 - 01 00 04 00 04 00 00 00   74 79 70 65 2F 00 00 00 - 02 00 00 00 00 00 0E 00   00 00 70 72 6F 74 6F 31 - 2E 72 65 71 75 65 73 74   13 00 00 00 0F 00 00 00 - 04 00 00 00 02 00 01 00   02 00 01 00 00 00 70 34 - 00 00 00 02 00 00 00 00   00 0F 00 00 00 70 72 6F - 74 6F 31 2E 72 65 73 70   6F 6E 73 65 17 00 00 00 - 13 00 00 00 05 00 00 00   01 00 04 00 02 00 04 00 - 03 00 00 00 72 65 74 18   00 00 00 14 00 00 00 04 - 00 00 00 D4 07 08 00 0A   00 06 00 00 00 70 72 6F - 74 6F 31
复制代码
通过这个效果(下面称为result)反推sproto组装流程:
第2-4行,按“type_n
第31-34行,生存protos总数s->protocol_n
第38-43行,通过import_type api构建每一个type的数据,生存在s->type这个数组里
第44-49行,通过import_protocol api构建每一个proto的数据,生存在s->proto这个数组里
  1.   // lualib/sproto/sproto.c  struct sproto *  sproto_create(const void * proto, size_t sz) {      ...      if (create_from_bundle(s, proto, sz) == NULL) {          pool_release(&s->memory);          return NULL;      }      return s; }  static struct sproto * create_from_bundle(struct sproto *s, const uint8_t* stream, size_t sz) {     ...     int fn = struct_field(stream, sz);     int i;     ...     for (i=0;itype = pool_alloc(&s->memory, n * sizeof(*s->type));         } else {             protocoldata = content+SIZEOF_LENGTH;             s->protocol_n = n;             s->proto = pool_alloc(&s->memory, n * sizeof(*s->proto));         }         content += todword(content) + SIZEOF_LENGTH;     }      for (i=0;itype_n;i++) {         typedata = import_type(s, &s->type[i], typedata);         if (typedata == NULL) {             return NULL;         }     }     for (i=0;iprotocol_n;i++) {         protocoldata = import_protocol(s, &s->proto[i], protocoldata);         if (protocoldata == NULL) {             return NULL;         }     }      return s; }
复制代码
sproto数据结构如下:
  1. // lualib/sproto/sproto.cstruct sproto { // 整个sproto结构    struct pool memory;    int type_n; // types总数    int protocol_n; // proto总数    struct sproto_type * type; // N个type信息    struct protocol * proto; // N个proto信息};struct sproto_type { // 单个type结构    const char * name; // 名称    int n; // fields实际个数    int base; //如果tag是一连的,为最小的tag,否则是-1    int maxn; //fields实际个数+最小的tag+不一连的tag个数,好比tag依次是1,3,5,则maxn=3+1+2=6    struct field *f; // N个field信息};struct field { // 单个field结构    int tag; //唯一的tag    int type; // 范例,可以是内置的integer,string,boolean,也可以是自界说的type,也可以是数组    const char * name; // 名称    struct sproto_type * st; //如果是自界说的范例,st指向这个范例    int key;    int extra;};struct protocol { //单个proto结构    const char *name; //名称    int tag; //唯一的tag    int confirm;    // confirm == 1 where response nil    struct sproto_type * p[2]; //request,response的范例};
复制代码
通过sproto_dump api,打印出构建后的sproto的信息如下:
  1. === 5 types ===AddresBook 1 0 1    person (0) *PersonPerson 4 0 4    name (0) string    id (1) integer    email (2) string    phone (3) *Person.PhoneNumberPerson.PhoneNumber 2 0 2    number (0) string    type (1) integerproto1.request 1 0 1    p (0) integerproto1.response 1 0 1    ret (0) *Person=== 1 protocol ===    proto1 (1001) request:proto1.request response:proto1.response
复制代码
 构建乐成后,调用saveproto将sproto生存在全局数组G_sproto中,供所有lua VM加载(loadproto)使用。
这就是sproto的剖析和构建流程。接下来会写一篇文章先容sproto如何使用。
在2021年1月13/14号我会开一个四小时玩转skynet训练营,也就是两个星期之后,现在已经开放报名,对游戏开辟感兴趣的诸位同好可以订阅一下,
训练营内容大概如下:
1.  多核并发编程
2.  消息队列,线程池
3.  actor消息调理
4.  网络模块实现
5.  时间轮定时器实现
6.  lua/c接口编程
7.  skynet编程精要
8.  demo演示actor编程思维
期待大家一起来打造游戏开辟的技能盛宴。
依附报名截图可以进群973961276领取上一期skynet训练营的录播以及这期的预习资料哦!

来源:https://blog.csdn.net/linuxguitu/article/details/111991876
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )