zoukankan      html  css  js  c++  java
  • protobuf在网游中的用法

    消息传递过程:

    client ---> gated ---> zoned

    CS消息结构:

    [cpp] view plaincopy
    01.message Head  
    02.{  
    03.         uint32 cmd_id;  
    04.}  
    05.  
    06.message Body  
    07.{  
    08.          AuthReq auth_req;  
    09.}  
    10.  
    11.message CSMessage  
    12.{  
    13.          Head head;  
    14.          Body body;  
    15.}  

    发送数据格式:16bits len + CSMessage。
      

    gated--->zoned也有一套消息

    [cpp] view plaincopy
    01.message GZMessage  
    02.{  
    03.      uint32 uid;           // 账户ID  
    04.      uint32 sessionState;  // 表示client与gated的某个session的建立,传输过程和结束  
    05.}  
    gated发给zoned的消息格式: total_len + GZMessage_len + GZMessage + CSMessage。
      

    gated处理过程

    client连接到gated,发的第一个包是签名包。

    gated收到Len + CSMessage。

    gated收到第一个包时,解析CSMessage,如果签名通过,向zoned发start_GZMessage。

    接下来收到的包,gated不作任何解析,直接转发给zoned。

    整个的Session生命周期中,gated只做一次CSMessage encoding&decoding,其他时候都是透传。
      

    zoned处理过程

    zoned收到total_len + GZMessage_len + GZMessage + CSMessage。

    total_len是分tcp stream用的, GZMessage_len是给zoned解析GZMessage用的。zoned先解析出GZMessage,然后将CSMessage传给逻辑层。
      

    上面的过程的一个优点是,没有大量的没必要的protobuf的Message解析。
      

    gated用到CSMessage的head信息

    有的gated须要知道CSMessage head的信息,比如要知道cmd_id,以便在gated统一地控制每个命令的发包频率,怎么办?

    直接的做法,解析整个CSMessage包,拿到cmd_id。这个成本高了点了。改变一下方式。

    [cpp] view plaincopy
    01.message CSMessageHead  
    02.{  
    03.         uint32 cmd_id;  
    04.}  
    05.  
    06.message CSMessageBody  
    07.{  
    08.          AuthReq auth_req;  
    09.}  

    就是把消息体分成两部分,head和body不在一个Message里了。

    发送的消息格式; total_len + msg_head_len + CSHead + CSBody。

    gated只要根据msg_head_len把CSHead解析出来就可以获取头部信息了。

    发给zoned的格式: total_len + GZMsg_len + GZMsg + CSHead_len + CSHead + CSBody。

    gated转发数据给zoned时,没有多余的数据解析和数据拷贝。


    补充:

    以上有点偏复杂了。可以简化一下。

    结构: total_len[16] + head_len[8] + head + body。

    这次像上一篇介绍最后介绍的一样,把head和body分开了,这样可以让服务器对消息的处理更灵活,有时候,服务器只要head内容,这样可以针对性地只解析head,而不用把body内容也解析出来。

    头定义差不多像下面的样子:

    package ProtoCS;

    message Head
    {
                required uint32 cmd_id = 1;
                。。。。。。。。
    }

    各种包体定义类似下面:

    message GetRoleRes
    {
                 required uint64 id;
                 required string name;
                 。。。。。
    }

    message GetPetRes
    {
                 。。。。
    }

    和上一篇介绍区别很大,变简单了。

    组织数据的过程的过程大体介绍一下:

    1. 写入16bits的总长度,这里16bits考虑用网络字节序。

    2. 写入8bits的头长度。

    3. 写入头内容

            ProtoCS::Head head;
            。。。。
            head.SerializeToArray(buf + 3, bufSize - 3);

    4. 最后写入包体内容

            ProtoCS::GetRoleRes roleRes;
            roleRes.set_id(111122233);
            roleRes.SerializeToArray(buf + 3 + headLen, bufSize - 3 - headLen);

    把整个buf+totalLen发送出去就好了。

  • 相关阅读:
    如何利用书签向word文档读取或写入数据[C#](
    一个较为完整的例子,还可以,简单一些的
    GridView的RowDataBound事件可做的一些事情
    ASP.NET中DataGrid和DataList控件用法比较
    GridView中绑定数据字段时可做的几种操作方法
    GridView中如何取得隐藏列的值
    一些常用的算法技巧总结
    位运算装逼指南
    小胖求学系列之文档生成利器(上)smartdoc
    系统监控之硬盘使用率
  • 原文地址:https://www.cnblogs.com/kex1n/p/2575411.html
Copyright © 2011-2022 走看看