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."); } } }
分別發送符合該協議格式的幀(用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
-------------------------------------------------------------
以上代碼請自行引入SuperSocket的dll和System.configuration.dll
本文由http://www.cnblogs.com/xiepeixing/原創,轉載請著名出處