SuperSocket使用 IRequestInfo 和 IReceiveFilter 等对象实现自定义协议

2023-11-15

        本文章向大家介绍SuperSocket使用 IRequestInfo 和 IReceiveFilter 等对象实现自定义协议,主要包括SuperSocket使用 IRequestInfo 和 IReceiveFilter 等对象实现自定义协议使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

为什么你要使用自定义协议?

        通信协议用于将接收到的二进制数据转化成您的应用程序可以理解的请求。 SuperSocket提供了一个内置的通信协议“命令行协议”定义每个请求都必须以回车换行"\r\n"结尾。但是一些应用程序无法使用命令行协议由于不同的原因。 这种情况下,你需要使用下面的工具来实现你的自定义协议:

* RequestInfo
* ReceiveFilter
* ReceiveFilterFactory
* AppServer and AppSession

第一步、定义一个和协议合适的数据结构

        数据结构是通信数据存储的结构形式,也是自定义协议的基础,每个客户端发送数据最后都解析到此数据结构(使用"简体中文(GB2312)",Encoding.GetEncoding("gb2312"编码))

public class MyData//20字节
{
    /// <summary>
    /// 开始符号6字节, "!Start"
    /// </summary>
    public string Start { get; set; }
    /// <summary>
    /// 消息类型,1字节
    /// </summary>
    public byte key { get; set; }
    /// <summary>
    /// 主体消息数据包长度,4字节
    /// </summary>
    public uint Lenght { get; set; }
    /// <summary>
    /// 4字节唯一设备识别符(Unique Device Identifier)
    /// </summary>
    public uint DeviceUDID { get; set; }
    /// <summary>
    /// 目标命令类型1字节
    /// </summary>
    public byte Type { get; set; }   
    /// <summary>
    /// 主体消息
    /// </summary>
    public byte[] Body { get; set; }       
    /// <summary>
    /// 结束符号4字节, "$End" ,
    /// </summary>
    public string End { get; set; }
 
    public override string ToString()
    {
        return string.Format("开始符号:{0},消息类型:{1},数据包长度:{2},唯一设备识别符:{3},目标命令类型:{4},主体消息:{5},结束符号:{6}",
            Start, key, Lenght, Lenght, DeviceUDID, Type, Body, End);
    }
}

 第二步、请求(RequestInfo)

        RequestInfo是表示来自客户端请求的实体类。 每个来自客户端的请求都能应该被实例化为 RequestInfo 类型。 RequestInfo 类必须实现接口IRequestInfo,该接口只有一个名为"Key"的字符串类型的属性:

        可以根据你的应用程序的需要来定义你自己的请求类型。 例如:

public class MyRequestInfo : RequestInfo<MyData>
    {
        public MyRequestInfo(string key, MyData myData)
        {
            //如果需要使用命令行协议的话,那么key与命令类名称myData相同
            Initialize(key, myData);
        }
    }

第三步、接收过滤器(ReceiveFilter)

        接收过滤器(ReceiveFilter)用于将接收到的二进制数据转化成请求实例(RequestInfo)。

        实现一个接收过滤器(ReceiveFilter), 你需要实现接口 IReceiveFilter:

public class MyReceiveFilter : IReceiveFilter<MyRequestInfo>
    {
        public Encoding encoding = Encoding.GetEncoding("gb2312");
        /// <summary>
        /// Gets the size of the left buffer.
        /// </summary>
        /// <value>
        /// The size of the left buffer.
        /// </value>
        public int LeftBufferSize { get; }
        /// <summary>
        /// Gets the next receive filter.
        /// </summary>
        public IReceiveFilter<MyRequestInfo> NextReceiveFilter { get; }
 
        public FilterState State { get; private set; }
        /// <summary>
        /// 该方法将会在 SuperSocket 收到一块二进制数据时被执行,接收到的数据在 readBuffer 中从 offset 开始, 长度为 length 的部分。
        /// </summary>
        /// <param name="readBuffer">接收缓冲区, 接收到的数据存放在此数组里</param>
        /// <param name="offset">接收到的数据在接收缓冲区的起始位置</param>
        /// <param name="length">本轮接收到的数据的长度</param>
        /// <param name="toBeCopied">表示当你想缓存接收到的数据时,是否需要为接收到的数据重新创建一个备份而不是直接使用接收缓冲区</param>
        /// <param name="rest">这是一个输出参数, 它应该被设置为当解析到一个为政的请求后,接收缓冲区还剩余多少数据未被解析</param>
        /// <returns></returns>
        /// 当你在接收缓冲区中找到一条完整的请求时,你必须返回一个你的请求类型的实例.
        /// 当你在接收缓冲区中没有找到一个完整的请求时, 你需要返回 NULL.
        /// 当你在接收缓冲区中找到一条完整的请求, 但接收到的数据并不仅仅包含一个请求时,设置剩余数据的长度到输出变量 "rest". SuperSocket 将会检查这个输出参数 "rest", 如果它大于 0, 此 Filter 方法 将会被再次执行, 参数 "offset" 和 "length" 会被调整为合适的值.
        public MyRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest)
        {
            rest = 0;
            if (length < 21)//没有数据
                return null;
            byte[] data = new byte[length];
            Buffer.BlockCopy(readBuffer, offset, data, 0, length);           
            var str = encoding.GetString(data);
            MyData myData=new MyData();
            myData.Start= encoding.GetString(data,0,6);//6字节
            myData.key= data[6];//1字节
            myData.Lenght = BitConverter.ToUInt32(data, 7);//4字节  6 + 1
            if (length < myData.Lenght + 20)
                return null;
            myData.DeviceUDID= BitConverter.ToUInt32(data, 11);//4字节 6 + 1+4
            myData.Type = data[15];//1字节 6+1+4+4
 
            myData.Body = new byte[myData.Lenght];//myData.Lenght字节
            Buffer.BlockCopy(data, 16, myData.Body, 0, (int)myData.Lenght);
 
            myData.End= encoding.GetString(data, (int)(16+ myData.Lenght), 4);//4字节
            if (myData.Start != "!Start" || myData.End != "$End")
                return null;
            rest =(int)(length-(20+ myData.Lenght));//未处理数据
            return new MyRequestInfo(myData.key.ToString(),myData);
        }

        public void Reset()
        {

        }
  • TRequestInfo: 类型参数 "TRequestInfo" 是你要在程序中使用的请求类型(RequestInfo);
  • LeftBufferSize: 该接收过滤器已缓存数据的长度;
  • NextReceiveFilter: 当下一块数据收到时,用于处理数据的接收过滤器实例;
  • Reset(): 重设接收过滤器实例到初始状态;
  • Filter(....): 该方法将会在 SuperSocket 收到一块二进制数据时被执行,接收到的数据在 readBuffer 中从 offset 开始, 长度为 length 的部分。

  • TRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest);
  • readBuffer: 接收缓冲区, 接收到的数据存放在此数组里
  • offset: 接收到的数据在接收缓冲区的起始位置
  • length: 本轮接收到的数据的长度
  • toBeCopied: 表示当你想缓存接收到的数据时,是否需要为接收到的数据重新创建一个备份而不是直接使用接收缓冲区
  • rest: 这是一个输出参数, 它应该被设置为当解析到一个为政的请求后,接收缓冲区还剩余多少数据未被解析

这儿有很多种情况需要你处理:

  • 当你在接收缓冲区中找到一条完整的请求时,你必须返回一个你的请求类型的实例.
  • 当你在接收缓冲区中没有找到一个完整的请求时, 你需要返回 NULL.
  • 当你在接收缓冲区中找到一条完整的请求, 但接收到的数据并不仅仅包含一个请求时,设置剩余数据的长度到输出变量 "rest". SuperSocket 将会检查这个输出参数 "rest", 如果它大于 0, 此 Filter 方法 将会被再次执行, 参数 "offset" 和 "length" 会被调整为合适的值.

第四步、接收过滤器工厂(ReceiveFilterFactory)

        接收过滤器工厂(ReceiveFilterFactory)用于为每个会话创建接收过滤器。定义一个过滤器工厂(ReceiveFilterFactory)类型,你必须实现接口 IReceiveFilterFactory. 类型参数 "TRequestInfo" 是你要在整个程序中使用的请求类型

/// <summary>
    /// Receive filter factory interface
    /// </summary>
    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
    public class MyReceiveFilterFactory : IReceiveFilterFactory<MyRequestInfo>
    {
        /// <summary>
        /// Creates the receive filter.
        /// </summary>
        /// <param name="appServer">The app server.</param>
        /// <param name="appSession">The app session.</param>
        /// <param name="remoteEndPoint">The remote end point.</param>
        /// <returns>
        /// the new created request filer assosiated with this socketSession
        /// </returns>
        //MyReceiveFilter<MyRequestInfo> CreateFilter(IAppServer appServer, IAppSession appSession, IPEndPoint remoteEndPoint);
        public IReceiveFilter<MyRequestInfo> CreateFilter(IAppServer appServer, IAppSession appSession, IPEndPoint remoteEndPoint)
        {
            return new MyReceiveFilter();
        }
    }

 第五步、和 AppSession,AppServer 配合工作

        现在, 你已经有了 RequestInfo, ReceiveFilter 和 ReceiveFilterFactory, 但是你还没有正式使用它们. 如果你想让他们在你的程序里面可用, 你需要定义你们的 AppSession 和 AppServer 来使用他们。

public class MySession : AppSession<MySession, MyRequestInfo>
   {
       public uint DeviceUDID;
       protected override void HandleException(Exception e)
       {
 
       }
   }
 
   public class MyServer : AppServer<MySession, MyRequestInfo>
   {
       /// <summary>
       /// 使用自定义协议工厂
       /// </summary>
       public MyServer()
           : base(new MyReceiveFilterFactory())
       {
       }
   }

第六步、服务端使用实例

MyServer myServer = new MyServer();
        Encoding encoding = Encoding.GetEncoding("gb2312");
 //Setup the appServer
            if (!myServer.Setup(1990)) //Setup with listening port
            {
                MessageBox.Show("Failed to setup!");
                return;
            }
            //Try to start the appServer
            if (!myServer.Start())
            {
                MessageBox.Show("Failed to start!");
                return;
            }
            myServer.NewSessionConnected += MyServer_NewSessionConnected;
            myServer.NewRequestReceived += MyServer_NewRequestReceived;

private void MyServer_NewRequestReceived(MySession session, MyRequestInfo requestInfo)
        {
            var msg=encoding.GetString(requestInfo.Body.Body);
        }
 
        private void MyServer_NewSessionConnected(MySession session)
        {
            session.Send("Welcome to SuperSocket Telnet Server");
        }

参考:http://docs.supersocket.net/v1-6/zh-CN/Implement-Your-Own-Communication-Protocol-with-IRequestInfo,-IReceiveFilter-and-etc

SuperSocket使用 IRequestInfo 和 IReceiveFilter 等对象实现自定义协议 - 码农教程 (manongjc.com)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SuperSocket使用 IRequestInfo 和 IReceiveFilter 等对象实现自定义协议 的相关文章

  • VC实现对话框文件拖拽

    使用过QQ的人都知道 只要把文件拖拽到消息框中就可以传送文件了 那么这种功能是如何实现的呢 其实很简单 只需要响应一个WM DROPFILES消息就可以了 在基于对话框的程序中 默认是没有这个消息的 按下Ctrl W 弹出类向导对话框 选择
  • kettle通过java步骤获取汉字首拼

    kettle通过java步骤获取汉字首拼 用途描述 一组数据 需要获取汉字首拼后 输出 实现效果 添加jar包 pinyin4j 2 5 0 jar 自定义常量数据 Java代码 完整代码 import net sourceforge pi
  • 用指针交换两个数

    题目描述 利用指针交换用户输入的两个数 输入 测试次数t 共t行 每行两个整数 输出 共t行 每行输出交换后的两个整数 输入样例1 2 1 2 35 21 输出样例1 2 1 21 35 思路分析 用a和b两个变量把数存储下来 再用两个指针
  • swift4.0自定义UITabBarController +UINavigationController

    首先开始项目之前我们需要搭建框架 首选UITabBarController UINavigationController 下面的代码是整理好的 包括我们会遇到的问题解决方法都在里面 自定义UINavigationController cla
  • TypeScript算法-19. 删除链表的倒数第 N 个结点

    TypeScript算法 19 删除链表的倒数第 N 个结点 思路 代码 思路 要删除倒数第N个节点 就要找到倒数第N 1个节点 然后直接将next指针指向next next即可 为了找到倒数第N 1个节点 可以使用快慢指针 让快指针先走n
  • openGL法线贴图和纹理贴图结合使用,以增强三维物体表面细节

    openGL系列文章目录 文章目录 openGL系列文章目录 前言 一 法线贴图 二 代码 1 主程序 2 着色器程序 运行效果 源码下载 前言 凹凸贴图的一种替代方法是使用查找表来替换法向量 这样我们就可以在不依赖数学函 数的情况下 对凸
  • JS复选框:获取选中的checkobx

    div div

随机推荐