为了实现客户端与服务器端的透明传输通信,必须在客户端方法调用时,自动对调用方法对应的参数、值、返回类型等进行流化处理,在服务器端能根据流化后的字节流,自动实现反向解析。
在对通信协议封装的过程中,因此引入了客户端协议栈,客户端协议栈,分两部分,发送部分和接收部分,其中发送部分为消息封包过程,需要进行消息封包拦截处理,而接收部分,为消息解包过程,需要进行进行消息解包处理。
其中封包过程,为从内到外,逐步进行的过程,可以将最外层封装在最外层基类中,采用类继承,层层递归处理的办法。
而解包过程,则是从外向内的过程,需要层层的剥离开,因此需要采用组合方式,层层剥离消息到结构体。
协议栈自动化过程中,封装了对应的序列化机制、版本代码、调用方式等等。
同步调用过程协议栈封装示例,对通信协议从内到外封装:
1.1.1.1 函数同步调用请求
请求方法描述字节流长度 | 请求方法描述字节流 | 请求参数数量 | 参数集合序列化处理 | 返回类型全名字节流长度 | 返回类型全名字节流 |
Int32 | 字节流 | Int32 |
| Int32 | 字节流 |
用于标记请求方法描述字节流长度 | 用于描述调用方法对应的字符串, UTF8编码后的字节流 | 用于描述调用方法的参数数量 | 需要到“参数集合序列化处理”进一步处理 | 用于标记返回类型全名字节流长度 | 用于描述返回类型的全名字符串,UTF8编码 |
|
1.1.1.1.1 参数集合序列化处理
参数类型全名字节流长度 | 参数类型全名字节流 | 参数序列化内容字节流长度 | 参数序列化内容字节流 |
Int32 | 字节流 | Int32 | 字节流 |
用于标记参数类型全名字节流长度 | 用于描述参数类型全名对应的字符串,UTF8编码后的字节流 | 用于标记参数序列化内容字节流长度 | 将参数内容进行序列化后,并经UTF8编码后的字节流 |
|
实现代码:
/// <summary>
/// 同步请求
/// </summary>
public class SyncRequestVersion1:RequestDataVersion1
{
/// <summary>
/// 同步请求
/// </summary>
/// <param name="serializeType">序列化方式</param>
/// <param name="requestType">请求方式</param>
/// <param name="objCallID">请求对象ID</param>
/// <param name="objMethodID">调用方法数据包ID</param>
/// <param name="key">服务映射键</param>
/// <param name="requestMethod">请求方法</param>
/// <param name="requestParameters">请求方法参数列表</param>
public SyncRequestVersion1(RMISerializeType serializeType, Guid objCallID, Guid objMethodID, string key,
Delegate requestMethod, object[] requestParameters):base(serializeType,RMIRequestType.SyncRequest,objCallID,objMethodID, key)
{
string requestCallName = requestMethod.Method.Name;
byte[] requestCallNameBuffer = System.Text.Encoding.UTF8.GetBytes(requestCallName);
messageStatckPack.PushInt32(requestCallNameBuffer.Length);
messageStatckPack.PushBytes(requestCallNameBuffer);
messageStatckPack.PushInt32(requestParameters.Length);//参数数量
IRMISerializble requestSerializer = null;
if (serializeType == RMISerializeType.Json)
{
requestSerializer = new RMIJsonSerialize();
}
foreach (var paItem in requestParameters)
{
byte[] paBuffer = requestSerializer.SerializeToUTF8(paItem);
if (paBuffer == null)
{
continue;
}
string requestPaTypeName = paItem.GetType().AssemblyQualifiedName;
byte[] requestPaTypeBuffer = System.Text.Encoding.UTF8.GetBytes(requestPaTypeName);
messageStatckPack.PushInt32(requestPaTypeBuffer.Length);
messageStatckPack.PushBytes(requestPaTypeBuffer);
messageStatckPack.PushInt32((int)paBuffer.Length);
if (paBuffer.Length > 0)
{
messageStatckPack.PushBytes(paBuffer);
}
}
string returnTypeFullName = requestMethod.Method.ReturnType.AssemblyQualifiedName;
byte[] returnTypeFullNameBuffer = System.Text.Encoding.UTF8.GetBytes(returnTypeFullName);
messageStatckPack.PushInt32(returnTypeFullNameBuffer.Length);
messageStatckPack.PushBytes(returnTypeFullNameBuffer);
}
}
1.1.1 客户端版本1,执行体协议
启用的序列化协议 | 请求方式 | 请求对象ID | 调用过程ID | 服务器端映射键字节流长度 | 服务器端映射键字节流 | 具体的调用请求 |
1Byte | 1Byte | GUID(16Byte) | GUID(16Byte) | Int32 | 字节流 | 字节流 |
0x00,Json序列化, 0x01,ProtoBuf序列化 | 0x01,注册订阅 0x02,同步调用请求 0x03,异步调用请求 | 用于标记客户端发起请求的对象 | 用于标记客户端发起请求的调用函数 | 用于标记服务器端映射键字节流长度 | 具体的字符内容,UTF8编码后的字节流 |
|
|
/// <summary>
/// 客户端版本1执行体数据处理器
/// </summary>
public abstract class RequestDataVersion1
{
/// <summary>
/// 处理栈
/// </summary>
protected Trace.Common.Communication.MessageStatck.MessageStatckPack messageStatckPack = new Trace.Common.Communication.MessageStatck.MessageStatckPack();
/// <summary>
///
/// </summary>
/// <param name="serializeType">序列化方式</param>
/// <param name="requestType">请求方式</param>
/// <param name="objCallID">请求对象ID</param>
/// <param name="objMethodID">调用方法数据包ID</param>
/// <param name="key">服务映射键</param>
/// <param name="callBuffer">调用数据包</param>
/// <returns></returns>
public RequestDataVersion1(RMISerializeType serializeType, RMIRequestType requestType, Guid objCallID, Guid objMethodID, string key)
{
messageStatckPack.PushByte((byte)serializeType);
messageStatckPack.PushByte((byte)requestType);
messageStatckPack.PushBytes(objCallID.ToByteArray());
messageStatckPack.PushBytes(objMethodID.ToByteArray());
}
/// <summary>
/// 获取封包数据
/// </summary>
/// <returns></returns>
public byte[] GetDataBuffer()
{
return messageStatckPack.GetDataBuffer();
}
}
1.1 数据包内容协议格式定义:
包唯一ID | 包标记 | 数据块内容 | 数据包校验位 |
GUI(16Byte) | 1Byte | 字节流 | 1Byte |
当前传输数据包的唯一ID | 0x00,表示整包传输, 0x01,表示分包传输 0x02,表示整包传输确认 0x03,表示分包传输确认 0x04,表示整包传输失败 0x05,表示分包传输失败 | 需要下面进一步处理 | 校验和,对数据包内容中的所有数据,进行校验和 |
|
1.1.1 整包传输数据块协议
版本号 | 命令状态 | 执行体 |
Int32 | Byte | 字节流 |
用于标记处理数据的版本 | 0x00,请求命令 0x01,应答命令 | 需要到“执行体协议”进一步处理 |
|
因为数据包部分,独立于数据执行体部分,并且分整包传输和分包传输,与执行体部分不是严格的继承和嵌套关系,此处采用组合方式,为执行体提供部分的组合包装封装,实现代码:
/// <summary>
/// 整数据块处理器
/// </summary>
public class AllBlockDataPack
{
private Trace.Common.Communication.MessageStatck.MessageStatckPack messageStatckPack = new Trace.Common.Communication.MessageStatck.MessageStatckPack();
/// <summary>
///
/// </summary>
/// <param name="version">版本号</param>
/// <param name="commandFlag">命令标记</param>
/// <param name="excuteBuffer">执行体</param>
/// <returns>
/// 返回整包处理封包数据
/// </returns>
public AllBlockDataPack(RMIVersion version, RMICommand commandFlag, byte[] excuteBuffer)
{
messageStatckPack.PushInt32((int)version);
messageStatckPack.PushByte((byte)commandFlag);
messageStatckPack.PushBytes(excuteBuffer);
}
/// <summary>
/// 获取封包数据
/// </summary>
/// <returns></returns>
public byte[] GetDataBuffer()
{
return messageStatckPack.GetDataBuffer();
}
}
/// <summary>
/// 数据包处理
/// </summary>
public class PackDataPack
{
Trace.Common.Communication.MessageStatck.MessageStatckPack messageStatckPack = new Trace.Common.Communication.MessageStatck.MessageStatckPack();
/// <summary>
///
/// </summary>
/// <param name="packID">数据包ID</param>
/// <param name="blockFlag">数据块标记</param>
/// <param name="blockBuffer">数据块</param>
/// <returns></returns>
public PackDataPack(Guid packID, RMIDataBlockFlag blockFlag, byte[] blockBuffer)
{
messageStatckPack.PushBytes(packID.ToByteArray());
messageStatckPack.PushByte((byte)blockFlag);
messageStatckPack.PushBytes(blockBuffer);
byte[] buffer=messageStatckPack.GetDataBuffer();
byte check = CheckSum(buffer);
messageStatckPack.PushByte(check);
}
/// <summary>
/// 获取封包数据
/// </summary>
/// <returns></returns>
public byte[] GetDataBuffer()
{
return messageStatckPack.GetDataBuffer();
}
/// <summary>
/// 进行校验和处理
/// </summary>
/// <param name="dataBuffer">体数据</param>
/// <returns></returns>
public byte CheckSum(byte[] dataBuffer)
{
byte checkSum = 0x00;
for (int i = 0; i < dataBuffer.Length; i++)
{
checkSum += dataBuffer[i];
}
return checkSum;
}
}
1 通信协议包格式
用于对客户端和服务器端通信时,数据传输过程中的数据包的封装处理,只负责具体的数据
包传输,此为整个封包的最外层包装。
起始标记位 | 包体长度 | 加密标记 | 数据包内容 |
4Byte | Int32 | 1Byte | 字节流 |
用于标记数据包的开始, 0x02,0x03,0xFF,0xFF | 用于标记数据包的长度,LittleEndian | 0x00,不加密 0x01,加密 | 所有数据的内容,需要下面的“数据包内容协议格式定义”进一步处理 |
|
最后一部分的协议,是外层的封包协议,直接追加到请求会话上,这也是进入通信信道的最后一层封装,此处可对进入通道的数据流,进行流化的加解密处理。
public class RMIClientDataInputFilter : Trace.Common.Communication.Message.BaseInPutMessageFilter<RMIClientDataRequest>
{
/// <summary>
/// 构建发送数据封包
/// </summary>
/// <param name="requestData">请求数据</param>
/// <returns>
/// 返回构建数据封包
/// </returns>
public override byte[] ProcessToSendBuffer(RMIClientDataRequest requestData)
{
MessageStatckPack messageStatckPack = new MessageStatckPack();
messageStatckPack.PushBytes(requestData.Start);
messageStatckPack.PushInt32((int)requestData.BodyBuffer.Length);
if (requestData.IsEncrypt == false)
{
messageStatckPack.PushByte((byte)RMIEncryptFlag.UnEncrpyt);
}
else
{
messageStatckPack.PushByte((byte)RMIEncryptFlag.Encrypt);
}
messageStatckPack.PushBytes(requestData.BodyBuffer);
return messageStatckPack.GetDataBuffer();
}
}
其中贯穿全局的,请求端是靠请求上下文来支撑的,毕竟封包方法的处理,是支离破碎的,请求上下文,用来协调进度、负责参数的传递、回调的适配等等
,下面回调上下文的实现
/// <summary>
/// RMI 同步请求上下文
/// </summary>
public class RMISyncRequestContext:RMIRequestContext
{
/// <summary>
///
/// </summary>
/// <param name="requestObjCallID">请求对象ID</param>
/// <param name="requestMethodID">请求调用方法ID</param>
/// <param name="serializeType">序列化方式</param>
/// <param name="versionCode">版本号</param>
/// <param name="serverKey">服务器映射键</param>
public RMISyncRequestContext(Guid requestObjCallID, Guid requestMethodID, RMISerializeType serializeType, RMIVersion versionCode, string serverKey)
: base(requestObjCallID, requestMethodID, serializeType, versionCode, serverKey)
{
}
private int waitTimeOut = 3600;
/// <summary>
/// 等待超时时间,单位是秒
/// </summary>
public int WaitTimeOut
{
get
{
return waitTimeOut * 1000;
}
}
/// <summary>
/// 等待信号量
/// </summary>
private ManualResetEvent waitManualResetEvent = new ManualResetEvent(false);
/// <summary>
/// 等待信号量
/// </summary>
public ManualResetEvent WaitManualResetEvent
{
get
{
return waitManualResetEvent;
}
}
private RMIClientDataResponse rmiClientDataResponse;
/// <summary>
/// 应答数据
/// </summary>
public RMIClientDataResponse RMIClientDataResponse
{
get
{
return rmiClientDataResponse;
}
set
{
rmiClientDataResponse = value;
}
}
}
/// <summary>
/// RMI请求上下文
/// </summary>
public abstract class RMIRequestContext
{
/// <summary>
///
/// </summary>
/// <param name="requestObjCallID">请求对象ID</param>
/// <param name="requestMethodID">请求调用方法ID</param>
/// <param name="serializeType">序列化方式</param>
/// <param name="versionCode">版本号</param>
/// <param name="serverKey">服务键</param>
public RMIRequestContext(Guid requestObjCallID, Guid requestMethodID, RMISerializeType serializeType, RMIVersion versionCode, string serverKey)
{
this.requestObjCallID = requestObjCallID;
this.requestMethodID = requestMethodID;
this.serializeType = serializeType;
this.versionCode = versionCode;
this.serverKey = serverKey;
}
private Guid requestObjCallID;
/// <summary>
/// 请求对象ID
/// </summary>
public Guid RequestObjCallID
{
get
{
return requestObjCallID;
}
}
private Guid requestMethodID;
/// <summary>
/// 调用方法ID
/// </summary>
public Guid RequestMethodID
{
get
{
return requestMethodID;
}
}
private RMISerializeType serializeType = RMISerializeType.Json;
/// <summary>
/// 序列化方式
/// </summary>
public RMISerializeType SerializeType
{
get
{
return serializeType;
}
}
private RMIVersion versionCode = RMIVersion.Version1;
/// <summary>
/// 版本代码
/// </summary>
public RMIVersion VersionCode
{
get
{
return versionCode;
}
}
private string serverKey;
/// <summary>
/// 通信映射键
/// </summary>
public string ServerKey
{
get
{
return serverKey;
}
}
private Int32 minPackBufferSize = 128;
/// <summary>
/// 最小封包大小,单位是M,默认128M
/// </summary>
public Int32 MinPackBufferSize
{
get
{
return minPackBufferSize * 1024 * 1024;
}
set
{
minPackBufferSize = value;
}
}
private bool isEncrypt = false;
/// <summary>
/// 是否加密
/// </summary>
public bool IsEncrypt
{
get
{
return isEncrypt;
}
set
{
isEncrypt = value;
}
}
private bool isSplitPack = false;
/// <summary>
/// 是否分包
/// </summary>
public bool IsSplitPack
{
get
{
return isSplitPack;
}
set
{
isSplitPack = value;
}
}
private RMIAllPackInfo allPackInfo;
/// <summary>
/// 整包传输,封包信息
/// </summary>
public RMIAllPackInfo AllPackInfo
{
get
{
return allPackInfo;
}
set
{
allPackInfo = value;
}
}
private List<RMISplitPackInfo> splitPackInfoList = new List<RMISplitPackInfo>();
/// <summary>
/// 分包,封包信息列表
/// </summary>
public List<RMISplitPackInfo> SplitPackInfoList
{
get
{
return splitPackInfoList;
}
set
{
splitPackInfoList = value;
}
}
private Int32 sendTimeOut = 3600;
/// <summary>
/// 发送超时,单位是秒,默认3600秒
/// </summary>
public Int32 SendTimeOut
{
get
{
return sendTimeOut;
}
set
{
sendTimeOut = value;
}
}
}
上面是整个协议栈的封包过程,采用继承方式,可以把协议栈中的公有封包部分单独剥离出来,减少了代码的出错率,组合和继承的结合使用,有效的精简了代码量,可以形成统一的代码风格和框架,达到追加新的协议功能时的快速扩展。