我们直接使用序列化后的ProtoBuff数据来通信,并且加密数据。
我们只使用简易的序列化,不使用.proto中间文件来映射类。
每个和客户端通信的协议都是一个类。具体到传输时,传输的协议内容分为三个部分,第一个部分是协议头部分,记录了整段信息的大小,第二个部分是协议名部分,协议名部分包含了协议名长度和具体的协议名,第三个部分才是具体的协议内容。
ProtocolEnum
使用一个枚举来表示协议类型。协议在传输时就是用一个数字表示的。
在Proto文件夹内新建ProtocolEnum
文件
1 2 3 4 5 6
| public enum ProtocolEnum { None = 0, MsgSecret = 1, MsgPing = 2, }
|
MsgBase
首先在Nuget包管理器中将ProtoBuf-net安装到SimpleServer工程内。
在Proto文件夹内新建MsgBase
文件,这个是各种协议的基类,还提供了协议内容编解码的方法。
EncodeName
方法,将协议名编码,将协议名按照UTF8编码的形式转化成byte数组之后,还要在前面加上协议名的长度,这个长度取两个byte,所以转化成Int16。协议名的部分没必要加密。记录协议名的长度是为了接收数据的时候方便计算后面具体协议内容的开始索引。
DecodeName
方法,将协议名解码,并且输出协议名的部分占占消息内容的长度(也就是out int count
)。
Encode
方法,编码协议内容并加密,属于整个传输内容的第三部分。
Decode
方法,解码协议内容,注意使用Serializer.NonGeneric.Deserialize
来反序列化,使用GetType
来获取到具体的协议类,否则的话无法成功反序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
| using ProtoBuf; using ServerBase; using SimpleServer.Net;
public class MsgBase { public virtual ProtocolEnum ProtoType { get; set; }
public static byte[] EncodeName(MsgBase msg) { byte[] nameBytes = System.Text.Encoding.UTF8.GetBytes(msg.ProtoType.ToString()); Int16 len = (Int16)nameBytes.Length; byte[] bytes = new byte[2 + len]; bytes[0] = (byte)(len % 256); bytes[1] = (byte)(len / 256); Array.Copy(nameBytes,0,bytes, 2, len); return bytes; } public static ProtocolEnum DecodeName(byte[] bytes,int offset,out int count) { count = 0; if(offset + 2 > bytes.Length) { return ProtocolEnum.None; } Int16 len = (Int16)((bytes[offset + 1] << 8) | bytes[offset]); if(offset + 2 + len > bytes.Length) { return ProtocolEnum.None; } count = 2 + len; try { string name = System.Text.Encoding.UTF8.GetString(bytes, offset + 2, len); return (ProtocolEnum)Enum.Parse(typeof(ProtocolEnum), name); } catch (Exception e) { Debug.LogError("不存在的协议:" + e); return ProtocolEnum.None; } } public static byte[] Encode(MsgBase msg) { using(MemoryStream ms = new MemoryStream()) { Serializer.Serialize(ms, msg); byte[] bytes = ms.ToArray(); string secretkey = ServerSocket.SecretKey; if(msg is MsgSecret) { secretkey = ServerSocket.PublicKey;
} bytes = AES.AESEncrypt(bytes, secretkey); return bytes; } } public static MsgBase? Decode(ProtocolEnum protocol,byte[] bytes,int offset,int count) { if (count <= 0) { Debug.LogError("协议解密出错,数据长度为0"); return null; } try { byte[] newbytes = new byte[count]; Array.Copy(bytes, offset, newbytes, 0, count); string secretkey = ServerSocket.SecretKey; if(protocol == ProtocolEnum.MsgSecret) { secretkey = ServerSocket.PublicKey; } newbytes = AES.AESDecrypt(newbytes, secretkey); using (MemoryStream ms = new MemoryStream(newbytes,0,newbytes.Length)) { Type? t = Type.GetType(protocol.ToString()); if (t != null) { return (MsgBase)Serializer.NonGeneric.Deserialize(t, ms); } else { Debug.LogError("无法找到类型,请检查ProtocolEnum声明是否一致"); return null; } } } catch (Exception e) { Debug.LogError("协议解析出错:" + e); return null; } } }
|
MsgSecret
新建MsgSecret类,这代表了密钥的传输协议
1 2 3 4 5 6 7 8 9 10 11
| [ProtoBuf.ProtoContract()] public class MsgSecret : MsgBase { public MsgSecret() { ProtoType = ProtocolEnum.MsgSecret; } [ProtoBuf.ProtoMember(1)] public override ProtocolEnum ProtoType { get; set; } [ProtoBuf.ProtoMember(2)] public string? Secret; }
|
注意:
- 每一个协议基类都必须要有一个构造函数和
ProtoType
的重载,否则ProtoBuf的序列化会出问题。
- 每一个协议基类的名称都要和
ProtocolEnum
内声明的名称相同。