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)