.net下的面向工控领域的远程方法调用(RMI)中间件,客户端协议栈请求端实现

为了实现客户端与服务器端的透明传输通信,必须在客户端方法调用时,自动对调用方法对应的参数、值、返回类型等进行流化处理,在服务器端能根据流化后的字节流,自动实现反向解析。

在对通信协议封装的过程中,因此引入了客户端协议栈,客户端协议栈,分两部分,发送部分和接收部分,其中发送部分为消息封包过程,需要进行消息封包拦截处理,而接收部分,为消息解包过程,需要进行进行消息解包处理。


其中封包过程,为从内到外,逐步进行的过程,可以将最外层封装在最外层基类中,采用类继承,层层递归处理的办法。


而解包过程,则是从外向内的过程,需要层层的剥离开,因此需要采用组合方式,层层剥离消息到结构体。


协议栈自动化过程中,封装了对应的序列化机制、版本代码、调用方式等等。


同步调用过程协议栈封装示例,对通信协议从内到外封装:

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;
            }
        }
    }


上面是整个协议栈的封包过程,采用继承方式,可以把协议栈中的公有封包部分单独剥离出来,减少了代码的出错率,组合和继承的结合使用,有效的精简了代码量,可以形成统一的代码风格和框架,达到追加新的协议功能时的快速扩展。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值