SuperSocket 1.6.4 通过FixedHeaderReceiveFilter解析自定义协议

SuperSocket 提供了一些通用的协议解析工具, 你可以用他们简单而且快速的实现你自己的通信协议:

  • TerminatorReceiveFilter (SuperSocket.SocketBase.Protocol.TerminatorReceiveFilter, SuperSocket.SocketBase)
  • CountSpliterReceiveFilter (SuperSocket.Facility.Protocol.CountSpliterReceiveFilter, SuperSocket.Facility)
  • FixedSizeReceiveFilter (SuperSocket.Facility.Protocol.FixedSizeReceiveFilter, SuperSocket.Facility)
  • BeginEndMarkReceiveFilter (SuperSocket.Facility.Protocol.BeginEndMarkReceiveFilter, SuperSocket.Facility)
  • FixedHeaderReceiveFilter (SuperSocket.Facility.Protocol.FixedHeaderReceiveFilter, SuperSocket.Facility)

由于本次项目涉及的通信协议是头部格式固定并且包含内容长度的协议这里主要讲解使用FixedHeaderReceiveFilter来拆解.

通信协议格式如下:

 
代码 字节数 说明
68H 1 帧起始码
DLC 4 设备逻辑地址
SEQ 2 主站地址与命令序号
68H 1 帧起始码
C  1 控制码
L 2 数据长度(DATA长度)
DATA 变长 数据内容
CS 1 校验码
16H 1 结束码

在FixedHeaderReceiveFilter,头部指数据内容之前的数据(即数据长度L之前的部分),以上协议可以知道,头部包含11个字节.

首先,根据协议的需要来定义自己的请求类型,先实现一个客户端请求的实体类RequestInfo,改RequestInfo类必须实现接口 IRequestInfo,该接口只有一个名为"Key"的字符串类型的属性.SuperSocket设计了两个RequestInfo类:StringRequestInfo 和BinaryRequestInfo,这里我们自定义一个来GDProtocolRequestInfo实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SuperSocket.SocketBase.Protocol;

namespace GDServer
{

    public class GDProtocolRequestInfo : IRequestInfo
    {
        /// <summary>
        /// [不使用]
        /// </summary>
        public string Key { get; set; }

        /// <summary>
        /// 设备逻辑地址
        /// </summary>
        public string DeviceLogicalCode { get; set; }

        /// <summary>
        /// 命令序列号
        /// </summary>
        public string Seq { get; set; }

        /// <summary>
        /// 控制码
        /// </summary>
        public string ControlCode { get; set; }

        /// <summary>
        /// 数据长度
        /// </summary>
        public string Length { get; set; }

        /// <summary>
        /// 数据域
        /// </summary>
        public string Data { get; set; }

        /// <summary>
        /// CS校验
        /// </summary>
        public string Cs { get; set; }

        /// <summary>
        /// 当前完整帧
        /// </summary>
        //public string EntireFrame { get; set; }
    }
}

然后设计基于类FixedHeaderReceiveFilter实现自己的接收过滤器GDProtocolReceiveFilterV2,主要实现GetBodyLengthFromHeader和ResolveRequestInfo方法,实现如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SuperSocket.SocketBase.Protocol;
using SuperSocket.Facility.Protocol;//
using SuperSocket.Common;//

namespace GDServer
{
    /// <summary>
    /// 广东规约过滤器V2,(帧格式为GDProtocolRequestInfo)
    /// </summary>
    public class GDProtocolReceiveFilterV2 : FixedHeaderReceiveFilter<GDProtocolRequestInfo>
    {
        public GDProtocolReceiveFilterV2()
            : base(11)
        {

        }

        /// <summary>
        /// 获取数据域和结尾字节长度
        /// </summary>
        /// <param name="header"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
        {
            //length为头部(包含两字节的length)长度

            //获取高位
            byte high = header[offset + length - 1];
            //获取低位
            byte low = header[offset + length - 2];
            int len = (int)high * 256 + low;
            return len + 2;//结尾有2个字节
        }

        /// <summary>
        /// 实现帧内容解析
        /// </summary>
        /// <param name="header"></param>
        /// <param name="bodyBuffer"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        protected override GDProtocolRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
        {
            GDProtocolRequestInfo res = new GDProtocolRequestInfo();
            string entireFrame = BytesToHexStr(header.Array) + BytesToHexStr(bodyBuffer.CloneRange(offset, length));
            //res.EntireFrame = entireFrame;
            res.DeviceLogicalCode = entireFrame.Substring(2, 8);
            res.Seq = entireFrame.Substring(10, 4);
            res.ControlCode = entireFrame.Substring(16, 2);
            res.Length = entireFrame.Substring(18, 4);
            int dataLen = int.Parse(HEXtoDEC(ReverseHexString(res.Length)));
            res.Data = entireFrame.Substring(22, dataLen * 2);
            res.Cs = entireFrame.Substring(22 + dataLen * 2, 2);
            return res;
        }

        /// <summary>
        /// 高低对调
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        string ReverseHexString(string str)
        {
            char[] buff = new char[str.Length];
            for (int i = 0; i < str.Length; i += 2)
            {
                buff[i] = str[str.Length - i - 2];
                buff[i + 1] = str[str.Length - 1 - i];
            }
            string s = new string(buff);
            return s;
        }

        /// <summary>
        /// 16进制转10进制
        /// </summary>
        /// <param name="HEX"></param>
        /// <returns></returns>
        string HEXtoDEC(string HEX)
        {
            return Convert.ToInt64(HEX, 16).ToString();
        }

        /// <summary>
        /// 转化bytes成16进制的字符
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        string BytesToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                }
            }
            return returnStr;
        }
    }
}

先创建新的AppSession,GDProtocolSessionV2,新的AppServer将使用GDProtocolSessionV2.GDProtocolSessionV2代码如下:

using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Protocol;
using System;

namespace GDServer
{
    public class GDProtocolSessionV2 : AppSession<GDProtocolSessionV2, GDProtocolRequestInfo>
    {
        protected override void HandleException(Exception e)
        {

        }
    }
}

使用该协议的方法是使用接收或者自己定义的接收过滤器工厂来在 SuperSocket 中启用该协议

using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Protocol;

namespace GDServer
{
    public class GDProtocolServerV2 : AppServer<GDProtocolSessionV2, GDProtocolRequestInfo>
    {
        public GDProtocolServerV2()
            : base(new DefaultReceiveFilterFactory<GDProtocolReceiveFilterV2, GDProtocolRequestInfo>()) //使用默认的接受过滤器工厂 (DefaultReceiveFilterFactory)
        {
        }
    }
}

这样,GDProtocolServerV2就完成了,下面是测试代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GDServer;
namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            var gdServer = new GDProtocolServerV2();
            gdServer.Setup(2015);
            gdServer.NewSessionConnected += gdServer_NewSessionConnected;
            gdServer.NewRequestReceived += gdServer_NewRequestReceived;
            gdServer.SessionClosed += gdServer_SessionClosed;
            gdServer.Start();
            Console.WriteLine("server is:" + gdServer.State.ToString());
            while (true)
            {
                if (Console.ReadKey().KeyChar == ‘q‘)
                {
                    gdServer.Stop();
                    gdServer.Dispose();
                    return;
                }
            }
        }

        static void gdServer_SessionClosed(GDProtocolSessionV2 session, SuperSocket.SocketBase.CloseReason value)
        {
            Console.WriteLine(session.RemoteEndPoint.ToString() + " closed. reason:" + value);
        }

        static void gdServer_NewRequestReceived(GDProtocolSessionV2 session, GDProtocolRequestInfo requestInfo)
        {
            var info = requestInfo;
            Console.WriteLine("receive from: " + session.RemoteEndPoint.ToString());
            Console.WriteLine("DeviceLogicalCode:" + info.DeviceLogicalCode);
            Console.WriteLine("Seq:" + info.Seq);
            Console.WriteLine("ControlCode:" + info.ControlCode);
            Console.WriteLine("Length:" + info.Length);
            Console.WriteLine("Data:" + info.Data);
            Console.WriteLine("Cs:" + info.Cs);
            Console.WriteLine("-------------------------------------------------------------");
        }

        static void gdServer_NewSessionConnected(GDProtocolSessionV2 session)
        {
            Console.WriteLine(session.RemoteEndPoint.ToString() + " connected.");
        }
    }
}

分别发送符合该协议格式的帧

68 77 77 12 34 00 01 68 A1 03 00 11 11 11 DC 16

68 77 77 12 34 41 01 68 01 0C 00 01 00 00 00 00 00 00 00 30 80 10 80 94 16

68 77 77 12 34 41 01 68 88 08 00 00 00 30 80 00 10 80 00 16 16

68 77 77 12 34 41 01 68 95 23 00 00 0B 00 00 10 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 5B 00 00 00 00 00 00 00 00 00 00 00 00 00 32 9E 16

打印结果如下:

server is:Running
127.0.0.1:34360 connected.
receive from: 127.0.0.1:34360
DeviceLogicalCode:77771234
Seq:0001
ControlCode:A1
Length:0300
Data:111111
Cs:DC
-------------------------------------------------------------
receive from: 127.0.0.1:34360
DeviceLogicalCode:77771234
Seq:4101
ControlCode:01
Length:0C00
Data:010000000000000030801080
Cs:94
-------------------------------------------------------------
receive from: 127.0.0.1:34360
DeviceLogicalCode:77771234
Seq:4101
ControlCode:88
Length:0800
Data:0000308000108000
Cs:16
-------------------------------------------------------------
receive from: 127.0.0.1:34360
DeviceLogicalCode:77771234
Seq:4101
ControlCode:95
Length:2300
Data:000B0000100000000000FFFFFFFFFFFFFFFF00005B0000000000000000000000000032
Cs:9E
-------------------------------------------------------------

以上代码请自行引入SuperSocket的dll和System.configuration.dll

本文由http://www.cnblogs.com/xiepeixing/原创,转载请著名出处

时间: 2024-08-10 12:04:38

SuperSocket 1.6.4 通过FixedHeaderReceiveFilter解析自定义协议的相关文章

【Java TCP/IP Socket】构建和解析自定义协议消息(含代码)

在传输消息时,用Java内置的方法和工具确实很用,如:对象序列化,RMI远程调用等.但有时候,针对要传输的特定类型的数据,实现自己的方法可能更简单.容易或有效.下面给出一个实现了自定义构建和解析协议消息的Demo(书上例子). 该例子是一个简单的投票协议.这里,一个客户端向服务器发送一个请求消息,消息中包含了一个候选人的ID,范围在0~1000.程序支持两种请求:一种是查询请求,即向服务器询问候选人当前获得的投票总数,服务器发回一个响应消息,包含了原来的候选人ID和该候选人当前获得的选票总数:另

ViewDragHelper完全解析, 自定义ViewGroup神器

Android ViewDragHelper完全解析, 自定义ViewGroup神器 ViewDragHelper实战, 自己打造的Drawerlayout

Wireshark解析自定义加密协议

一.概述 网上撰文写wireshark使用lua脚本解析协议的文章比较多. 笔者最近也因工作需要使用wireshark解析协议.但因网络安全,协议的数据部分被加密了.无法简单的使用lua脚本进行解析. 考虑到加密算法和压缩算法的复杂性,采用调用lua C库的方法,完成解密(解压). 下面与大家分享下大致思路. 二.目标及思路 协议的大致格式如下: 协议字段 命名 协议版本(1字节) protoVersion 协议命令类型(2字节) protoCmdType 协议加密类型(1字节) protoEn

Python爬虫最为核心的HTTP协议解析,及自定义协议的分析!

机器之间的协议就是机器通信的语法,只有按照这种语法发来的信息,机器之间才能相互理解内容,也可以理解为信息的一种格式. HTTP/IP协议是互联网最为重要的协议,没有HTTP/IP协议,也就没有互联跟不会有网,对于爬虫而言一切数据.请求都是围绕HTTP协议展开.但是在python实现的网络爬虫中都是使用封装好了的请求库如:requests.scrapy.urllib等,这些是对socket的封装,而socket是除了机器语言外最底层的协议. HTTP是公认的协议,但是并不是所有的终端通信都使用HT

物联网架构成长之路(35)-利用Netty解析物联网自定义协议

一.前言 前面博客大部分介绍了基于EMQ中间件,通信协议使用的是MQTT,而传输的数据为纯文本数据,采用JSON格式.这种方式,大部分一看就知道是熟悉Web开发.软件开发的人喜欢用的方式.由于我也是做web软件开发的,也是比较喜欢这种方式.阿里的物联网平台,也是推荐这种方式.但是,但是做惯硬件开发,嵌入式开发就比较喜欢用裸TCP-Socket连接.采用的是二进制协议.基于此大部分应用场合为了兼容旧设备,就需要单独开发一个TCP服务器的网关.这里使用以前学过的,也是比较流行的Netty框架. 话不

Spring源码解析——自定义标签解析

再讲解析自定义标签之前,先要知道怎么实现自定义标签的,接下来就来看下吧. 1.第一步,定义一个实体类,如图1: 图1 2.第二步,定义一个xsd(xsd是什么不知道的自行百度),如图2: 图2 3.第三步,生成spring.handlers和spring.schemas文件,一定要放在resources/META-INF下面,因为解析的时候只会到这个目录下面去找,先看下spring.handlers,如图3: 图3 在看下spring.schemas,如图4: 图4 4.第四步,实现Abstra

Netty自定义协议解析原理与应用

目前,大家都选择Netty做为游戏服务器框架网络通信的框架,而且目前也有很多优秀的产品是基于Netty开发的.它的稳定性,易用性和高效率性已得到广泛的认同.在游戏服务器开发中,选择netty一般就意味着我们要使用长连接来建立与客户端的通信,并且是自定义协议,在网络开发中,我们不得不处理断包,粘包的问题,因为Tcp/ip是基于数据流的传输,包与包之间没有明确的界限,而且于由网络路由的复杂性,大包有可能分成小包,小包也有可能被组装成大包进行传输.而Netty就考虑到了这一点,而且它用一个类就帮我们处

自定义协议的编码解码

2015.4.1 wqchen. 转载请注明出处 http://www.cnblogs.com/wqchen/p/4385798.html 本文介绍的是一个自定义协议的编码解码工具的实现. 游戏开发中,前端后端协议一般都会协商定制通信协议的格式,统一格式后用程序脚本对应前端和后端的编程语言,分别生成一份协议的编码和解码方案,便于协议的一致性. 这样的工具有很多,比较出名的是google的protobuf,它可以支持很多种编程语言.我也曾试用过protobuf,看过一点它的实现,protobuf完

wireshark插件开发 - 自定义协议

虽然wireshark自带了很多知名协议的解析插件,譬如HTTP.DHCP等等,然而在实际应用环境中,有不少软件之间的通信协议都是私有的,如游戏客户端和服务器之间的交互协议通常都是私有的,wireshark无法具体解析出各种字段之间的含义,只能显示接收到的二进制数据,给协议的分析和问题的排查带来了一定的困难,尤其是协议内容比较复杂时. 本文一个自定义的简单协议入手,分析如何基于wireshark开发自定义协议分析插件. 1.1. 概述 本书使用Go语言来描述协议的交互过程.Go由Google出品