赞
踩
介绍
在阅读了罗培羽著作的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版)罗培羽 著
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。