SuperSocket 1.6.4 通過FixedHeaderReceiveFilter解析自定義協議

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來拆解.

通信協議格式如下:

 
代碼字節數說明
68H1幀起始碼
DLC4設備邏輯地址
SEQ2主站地址與命令序號
68H1幀起始碼
C 1控制碼
L2數據長度(DATA長度)
DATA變長數據內容
CS1校驗碼
16H1結束碼

在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.");
        }
    }
}
複製代碼

 

分別發送符合該協議格式的幀(用TCP調試助手使用hex方式發送)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章