一、概要
在了解了网络字节序之后,接下来就是要讲最最重点的消息协议。数据包是什么呢,数据包可以理解为两个人讲电话说的每一句话的内容。通过大家约定好的方式去理解。达到只有接听电话两个人才懂的东西。在程序中如何体现出来呢,那么接着往下看。
技术交流QQ群:580749909 欢迎交流有问必答,文章尾有个人的微信公众号有兴趣的小伙伴多多关注。
二、简介
消息数据包主要是以二进制数组的形式存在,主要分为4个部分。
校验位:校验是否是双方约定好的“暗号”,如果校验位都不通过就没必要再继续往下解析节约处理时间。
消息头:是描述整个消息长度以及其它附加信息。
消息体:描述消息的具体内容,比如说想获取“股票历史行情数据”这些具体想得到的内容序列化之后存放在消息体中。
消息尾:通常用来描述整个消息的结束,主要还是需要看应用场景消息尾可以不需要。
三、主要内容
代码设计层面来说大致是基于以上的路线来走,首先我们脑子里有一个概念
1.消息是一个整体,这个整体分为四个部分(校验位、消息头、消息体、消息尾)
2.实际传输时以byte数组(二进制)的形式进行传递,将要写入的内容转换为字节按照顺序依次写入数组。解析同样按照这个规则解析再转为具体内容。
3.数据格式,上一条提到了二进制形式传递那怎么把你要传的数据以一种标准的方式发送出去呢。这里举例几种数据格式json 还有 protobuf。推荐用protobuf序列化速度快数据包压缩的体积更小。
4.大端小端的约定,服务方和客户方约好我们传输写入字节序的大小端。推荐统一用大端。
5.代码设计
四、代码设计
第一步,声明一个接口来定义数据包基础结构。
public interfaceIPacket
{byte[] Serialize();void Deserialize(ref byte[] data);
}
第二步,分别定义响应(Respone)和请求(Request)数据包结构
响应(Respone)
public class RpcRespone: IPacketwhereTMessage : IMessage
{///
///4个byte表示package长度///
public int Length { get; private set; }///
///package头18个字节///
public RespHeader Header { get; set; }///
///package内容///
public TMessage Body { get; set; }public byte[] Serialize()
{var header =Header.Serialize();
Length+=header.Length;byte[] body = null;var json =JsonConvert.SerializeObject(Body);if (json != null)
{
body=Encoding.UTF8.GetBytes(json);
Length+=body.Length;
}var package = new byte[4+Length];
BytesWriter.Write(Length,ref package, 0); //length
BytesWriter.Write(header, ref package, 4); //header
if (body != null)
{
BytesWriter.Write(body,ref package, 4 + RespHeader.Length); //body
}returnpackage;
}public void Deserialize(ref byte[] data)
{try{
Length= BytesReader.ReadInt32(ref data, 0);var header = BytesReader.ReadBuffer(ref data, 4, RespHeader.Length);
Header= newRespHeader();
Header.Deserialize(refheader);var body = BytesReader.ReadBuffer(ref data, 4 + RespHeader.Length, Length -RespHeader.Length);var json =Encoding.UTF8.GetString(body);
Body= JsonConvert.DeserializeObject(json);
}catch(Exception ex)
{
LogHepler.Log.ErrorFormat("JsonConvert Deserialize. {0}", ex.Message);
}
}
}
public classReqHeader : IPacket
{public const int Length = 24;///
///包头标志,用于校验///
public byte Checkbit { get; set; }///
///8个byte表示uid///
public long Uid { get; set; }///
///8个byte表示是requestId///
public long RequestId { get; set; }///
///1个bit表示是否加密///
public bool IsEncrypt { get; set; }///
///2个byte表示commandId///
public short CommandId { get; set; }///
///2个byte表示扩展位1的长度///
public short Ext1 { get; set; }///
///2个byte表示扩展位2的长度///
public short Ext2 { get; set; }public byte[] Serialize()
{
Checkbit=Header.Checkbit;var header = new byte[24];try{
BytesWriter.Write(Checkbit,ref header, 0);
BytesWriter.Write(Uid,ref header, 1);
BytesWriter.Write(RequestId,ref header, 9);
BytesWriter.Write(IsEncrypt,ref header, 17);
BytesWriter.Write(CommandId,ref header, 18);
BytesWriter.Write(Ext1,ref header, 20);
BytesWriter.Write(Ext2,ref header, 22);
}catch(Exception ex)
{
LogHepler.Log.ErrorFormat("Serialize Request Header Exception. {0}", ex.Message);
}returnheader;
}public void Deserialize(ref byte[] data)
{try{
Checkbit= BytesReader.ReadByte(ref data, 0);
Uid= BytesReader.ReadInt64(ref data, 1);
RequestId= BytesReader.ReadInt64(ref data, 9);
IsEncrypt= BytesReader.ReadBool(ref data, 17);
CommandId= BytesReader.ReadInt16(ref data, 18);
Ext1= BytesReader.ReadInt16(ref data, 20);
Ext2= BytesReader.ReadInt16(ref data, 22);
}catch(Exception ex)
{
LogHepler.Log.ErrorFormat("Serialize Request Header Exception. {0}", ex.Message);
}
}
}
请求(Request)
public class RpcRequest: IPacketwhere TMessage : class, IMessage
{///
///4个byte表示package长度///
public int Length { get; private set; }///
///package头24个字节///
public ReqHeader Header{ get; set; }///
///package内容///
public TMessage Body { get; set; }public byte[] Serialize()
{var header =Header.Serialize();
Length+=header.Length;byte[] body = null;var json =JsonConvert.SerializeObject(Body);if (json != null)
{
body=Encoding.UTF8.GetBytes(json);
Length+=body.Length;
}var package = new byte[4 +Length];
BytesWriter.Write(Length,ref package, 0); //length
BytesWriter.Write(header, ref package, 4); //header
if (body != null)
{
BytesWriter.Write(body,ref package, 4 + ReqHeader.Length); //body
}returnpackage;
}public void Deserialize(ref byte[] data)
{try{
Length= BytesReader.ReadInt32(ref data, 0);var header = BytesReader.ReadBuffer(ref data, 4, ReqHeader.Length);
Header= newReqHeader();
Header.Deserialize(refheader);var body = BytesReader.ReadBuffer(ref data, 4 + ReqHeader.Length, Length -ReqHeader.Length);var json =Encoding.UTF8.GetString(body);//LogHepler.Log.DebugFormat("JsonConvert Deserialize. Id:[{0}] body:[{1}]", Header.RequestId, json);
Body = JsonConvert.DeserializeObject(json);
}catch(Exception ex)
{
LogHepler.Log.ErrorFormat("JsonConvert Deserialize. {0}", ex.Message);
}
}
}
public classRespHeader : IPacket
{public const int Length = 18;///
///包头标志,用于校验///
public byte Checkbit { get; set; }///
///8个字节,请求ID///
public long RequestId { get; set; }///
///4个字节,返回结果状态码///
public int Code { get; set; }///
///1个字节,是否加密///
public bool IsEncrypt { get; set; }///
///4个字节,命令ID///
public short CommandId { get;set; }///
///2个字节,扩展参数///
public short Ext1{get; set; }public byte[] Serialize()
{
Checkbit=Header.Checkbit;var header = new byte[18];try{
BytesWriter.Write(Checkbit,ref header, 0);
BytesWriter.Write(RequestId,ref header, 1);
BytesWriter.Write(Code,ref header, 9);
BytesWriter.Write(IsEncrypt,ref header, 13);
BytesWriter.Write(CommandId,ref header, 14);
BytesWriter.Write(Ext1,ref header, 16);
}catch(Exception ex)
{
LogHepler.Log.ErrorFormat("Serialize Respone Header Exception. {0}", ex.Message);
}returnheader;
}public void Deserialize(ref byte[] data)
{try{
Checkbit= BytesReader.ReadByte(ref data, 0);
RequestId= BytesReader.ReadInt64(ref data, 1);
Code= BytesReader.ReadInt32(ref data, 9);
IsEncrypt= BytesReader.ReadBool(ref data, 13);
CommandId= BytesReader.ReadInt16(ref data, 14);
Ext1= BytesReader.ReadInt16(ref data, 16);
}catch(Exception ex)
{
LogHepler.Log.ErrorFormat("Deserialize Respone Header Exception. {0}", ex.Message);
}
}
}
以上基本是数据包的设计思路和代码设计的实现。后面会专门写博客专门讲解实战应用。