Unity【Multiplayer 多人在线】- Socket 通用服务端框架(三)、Protobuf 通信协议
介绍
在阅读了罗培羽著作的Unity3D网络游戏实战一书后,博主综合自己的开发经验与考虑进行部分修改和调整,将通用的客户端网络模块和通用的服务端框架进行提取,形成专栏,介绍Socket网络编程,希望对其他人有所帮助。目录如下:
一、通用服务端框架
(三)、Protobuf 通信协议
二、通用客户端网络模块
本篇内容:
1.Protobuf简介:
Protocol Buffers是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。它不依赖于语言和平台并且可拓展性极强。
2.Protobuf优点:
同XML相比,Protobuf在序列化结构化数据方面有许多优点:
*1.更简单
*2.数据描述文件只需原来的1/10至1/3
*3.解析速度是原来的20倍至100倍
*4.减少了二义性
*5.生成了更容易在编程中使用的数据访问类
*6.支持多种编程语言
同Json相比,Protobuf的序列化速度也是提高了数倍
3.Protobuf语法规则:
1).指定字段类型
所有的字段都是标量类型:string、bool、int类型等等,当然也可以为字段指定其他的合成类型,包括枚举或其他消息类型。
2).分配标识号
在消息定义中,每个字段都有唯一的标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够更改。
注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留[1,15]之内的标识号。
切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。最小的标识号可以从1开始,最大到229 - 1,or 536,870,911。不可以使用其中的[19000-19999]标识号,Protobuf协议实现中对这些进行了预留。如果非要在.proto文件中使用这些预留标识号,编译时就会报警。
3.)指定字段规则
所指定的消息字段修饰符必须是如下之一:
* required : 不可增加或删除的字段,必须初始化;
* optional : 可选字段,可删除,可以不初始化;
* repeated : 可重复字段(对应C#里面的List);
4.编译工具protoc.exe:
1).创建.proto文件:

2).在控制台打开protoc.exe所在路径:

3).输入编译命令protoc -I=./ --csharp_out=./ .proto文件名称

生成的.cs文件:

5.ProtoUtility协议工具类:
将protobuf-net.dll加入项目引用:

封装ProtoUtility协议工具类,包含协议及协议名的编码与解码方法:
  
   - 
    
     
    
    
     
      using ProtoBuf;
     
    
- 
    
     
    
    
     
      using System.Text;
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      namespace SK.Framework.Sockets
     
    
- 
    
     
    
    
     
      {
     
    
- 
    
     
    
    
         /// <summary>
     
    
- 
    
     
    
    
         /// 协议工具
     
    
- 
    
     
    
    
         /// </summary>
     
    
- 
    
     
    
    
         public static class ProtoUtility
     
    
- 
    
     
    
    
     
          {
     
    
- 
    
     
    
    
             /// <summary>
     
    
- 
    
     
    
    
             /// 协议编码
     
    
- 
    
     
    
    
             /// </summary>
     
    
- 
    
     
    
    
             /// <param name="proto">协议</param>
     
    
- 
    
     
    
    
             /// <returns>返回编码后的字节数据</returns>
     
    
- 
    
     
    
    
             public static byte[] Encode(IExtensible proto)
     
    
- 
    
     
    
    
     
              {
     
    
- 
    
     
    
    
                 using (MemoryStream ms = new MemoryStream())
     
    
- 
    
     
    
    
     
                  {
     
    
- 
    
     
    
    
     
                      Serializer.Serialize(ms, proto);
     
    
- 
    
     
    
    
                     return ms.ToArray();
     
    
- 
    
     
    
    
     
                  }
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
             /// <summary>
     
    
- 
    
     
    
    
             /// 协议解码
     
    
- 
    
     
    
    
             /// </summary>
     
    
- 
    
     
    
    
             /// <param name="protoName">协议名</param>
     
    
- 
    
     
    
    
             /// <param name="bytes">要解码的byte数组</param>
     
    
- 
    
     
    
    
             /// <param name="offset">协议体所在起始位置</param>
     
    
- 
    
     
    
    
             /// <param name="count">协议体长度</param>
     
    
- 
    
     
    
    
             /// <returns>返回解码后的协议</returns>
     
    
- 
    
     
    
    
             public static IExtensible Decode(string protoName, byte[] bytes, int offset, int count)
     
    
- 
    
     
    
    
     
              {
     
    
- 
    
     
    
    
                 using (MemoryStream ms = new MemoryStream(bytes, offset, count))
     
    
- 
    
     
    
    
     
                  {
     
    
- 
    
     
    
    
     
                      Type type = Type.GetType(protoName);
     
    
- 
    
     
    
    
                     return (IExtensible)Serializer.NonGeneric.Deserialize(type, ms);
     
    
- 
    
     
    
    
     
                  }
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
             /// <summary>
     
    
- 
    
     
    
    
             /// 协议名编码
     
    
- 
    
     
    
    
             /// </summary>
     
    
- 
    
     
    
    
             /// <param name="proto">协议</param>
     
    
- 
    
     
    
    
             /// <returns>返回编码后的字节数据</returns>
     
    
- 
    
     
    
    
             public static byte[] EncodeName(IExtensible proto)
     
    
- 
    
     
    
    
     
              {
     
    
- 
    
     
    
    
                 //名字bytes和长度
     
    
- 
    
     
    
    
                 byte[] nameBytes = Encoding.UTF8.GetBytes(proto.GetType().FullName);
     
    
- 
    
     
    
    
     
                  Int16 length = (Int16)nameBytes.Length;
     
    
- 
    
     
    
    
                 //申请bytes数值
     
    
- 
    
     
    
    
                 byte[] bytes = new byte[length + 2];
     
    
- 
    
     
    
    
                 //组装2字节的长度信息
     
    
- 
    
     
    
    
     
                  bytes[0] = (byte)(length % 256);
     
    
- 
    
     
    
    
     
                  bytes[1] = (byte)(length / 256);
     
    
- 
    
     
    
    
                 //组装名字bytes
     
    
- 
    
     
    
    
     
                  Array.Copy(nameBytes, 0, bytes, 2, length);
     
    
- 
    
     
    
    
                 return bytes;
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
             /// <summary>
     
    
- 
    
     
    
    
             /// 协议名解码
     
    
- 
    
     
    
    
             /// </summary>
     
    
- 
    
     
    
    
             /// <param name="bytes">要解码的byte数组</param>
     
    
- 
    
     
    
    
             /// <param name="offset">起始位置</param>
     
    
- 
    
     
    
    
             /// <param name="length">长度</param>
     
    
- 
    
     
    
    
             /// <returns>返回解码后的协议名</returns>
     
    
- 
    
     
    
    
             public static string DecodeName(byte[] bytes, int offset, out int length)
     
    
- 
    
     
    
    
     
              {
     
    
- 
    
     
    
    
     
                  length = 0;
     
    
- 
    
     
    
    
                 //必须大于2字节
     
    
- 
    
     
    
    
                 if (offset + 2 > bytes.Length) return string.Empty;
     
    
- 
    
     
    
    
                 //获取长度
     
    
- 
    
     
    
    
     
                  Int16 l = (Int16)((bytes[offset + 1] << 8) | bytes[offset]);
     
    
- 
    
     
    
    
                 if (l <= 0) return string.Empty;
     
    
- 
    
     
    
    
                 //长度必须足够
     
    
- 
    
     
    
    
                 if (offset + 2 + l > bytes.Length) return string.Empty;
     
    
- 
    
     
    
    
                 //解析
     
    
- 
    
     
    
    
     
                  length = 2 + l;
     
    
- 
    
     
    
    
                 string name = Encoding.UTF8.GetString(bytes, offset + 2, l);
     
    
- 
    
     
    
    
                 return name;
     
    
- 
    
     
    
    
     
              }
     
    
- 
    
     
    
    
     
          }
     
    
- 
    
     
    
    
     
      }
     
    
 参考资料:《Unity3D网络游戏实战》(第2版)罗培羽 著
文章来源: coderz.blog.csdn.net,作者:CoderZ1010,版权归原作者所有,如需转载,请联系作者。
原文链接:coderz.blog.csdn.net/article/details/124054972
- 点赞
- 收藏
- 关注作者
 
             
           
评论(0)