国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C# > 正文

C# 常用協議實現模版及FixedSizeReceiveFilter示例(SuperSocket入門)

2020-01-24 00:49:22
字體:
來源:轉載
供稿:網友

Socket里面的協議解析是Socket通訊程序設計中最復雜的地方,如果你的應用層協議設計或實現不佳,Socket通訊中常見的粘包,分包就難以避免。SuperSocket內置了命令行格式的協議CommandLineProtocol,如果你使用了其它格式的協議,就必須自行實現自定義協議CustomProtocol。看了一篇文檔之后, 你可能會覺得用 SuperSocket 來實現你的自定義協議并不簡單。 為了讓這件事變得更容易一些, 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)---頭部格式固定并包含內容長度協議

1、TerminatorReceiveFilter結束符協議

結束符協議和命令行協議類似,一些協議用結束符來確定一個請求.例如, 一個協議使用兩個字符 "##" 作為結束符, 于是你可以使用類 "TerminatorReceiveFilterFactory":

結束符協議TerminatorProtocolServer :

public class TerminatorProtocolServer : AppServer{  public TerminatorProtocolServer()  : base(new TerminatorReceiveFilterFactory("##")) { }}

基于TerminatorReceiveFilter實現你的接收過濾器(ReceiveFilter):

public class YourReceiveFilter : TerminatorReceiveFilter<YourRequestInfo>{ //More code}

實現你的接收過濾器工廠(ReceiveFilterFactory)用于創建接受過濾器實例:

public class YourReceiveFilterFactory : IReceiveFilterFactory<YourRequestInfo>{ //More code}

2、CountSpliterReceiveFilter 固定數量分隔符協議

有些協議定義了像這樣格式的請求 "#part1#part2#part3#part4#part5#part6#part7#". 每個請求有7個由 '#' 分隔的部分. 這種協議的實現非常簡單:

/// <summary>/// 請求格式:#part1#part2#part3#part4#part5#part6#part7#/// </summary>public class CountSpliterAppServer : AppServer{ public CountSpliterAppServer()  : base(new CountSpliterReceiveFilterFactory((byte)'#', 8)) //8個分隔符,7個參數。除使用默認的過濾工廠,還可以參照上一個實例定制協議 { }}

3、FixedSizeReceiveFilter 固定請求大小協議

在這種協議之中, 所有請求的大小都是相同的。如果你的每個請求都是有8個字符組成的字符串,如"HUANG LI", 你應該做的事就是想如下代碼這樣實現一個接收過濾器(ReceiveFilter):

class MyReceiveFilter : FixedSizeReceiveFilter<StringRequestInfo>{ public MyReceiveFilter()  : base(8) //傳入固定的請求大小 { } protected override StringRequestInfo ProcessMatchedRequest(byte[] buffer, int offset, int length, bool toBeCopied) {  //TODO: 通過解析到的數據來構造請求實例,并返回 }}

然后在你的 AppServer 類中使用這個接受過濾器 (ReceiveFilter):

public class MyAppServer : AppServer{ public MyAppServer()  : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //使用默認的接受過濾器工廠 (DefaultReceiveFilterFactory) { }} 

4、BeginEndMarkReceiveFilter 帶起止符協議

在這類協議的每個請求之中 都有固定的開始和結束標記。例如, 我有個協議,它的所有消息都遵循這種格式 "&xxxxxxxxxxxxxx#"。因此,在這種情況下, "&" 是開始標記, "#" 是結束標記,于是你的接受過濾器可以定義成這樣:

class MyReceiveFilter : BeginEndMarkReceiveFilter<StringRequestInfo>{ //開始和結束標記也可以是兩個或兩個以上的字節 private readonly static byte[] BeginMark = new byte[] { (byte)'&' }; private readonly static byte[] EndMark = new byte[] { (byte)'#' }; public MyReceiveFilter()  : base(BeginMark, EndMark) //傳入開始標記和結束標記 { } protected override StringRequestInfo ProcessMatchedRequest(byte[] readBuffer, int offset, int length) {  //TODO: 通過解析到的數據來構造請求實例,并返回 }}

然后在你的 AppServer 類中使用這個接受過濾器 (ReceiveFilter):

public class MyAppServer : AppServer{ public MyAppServer()  : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //使用默認的接受過濾器工廠 (DefaultReceiveFilterFactory) { }}

5、FixedHeaderReceiveFilter 頭部格式固定并包含內容長度協議

這種協議將一個請求定義為兩大部分, 第一部分定義了包含第二部分長度等等基礎信息. 我們通常稱第一部分為頭部.

例如, 我們有一個這樣的協議: 頭部包含 6 個字節, 前 4 個字節用于存儲請求的名字, 后兩個字節用于代表請求體的長度:

/// +-------+---+-------------------------------+
/// |request| l |                               |
/// | name  | e |    request body               |
/// |  (4)  | n |                               |
/// |       |(2)|                               |
/// +-------+---+-------------------------------+
使用 SuperSocket, 你可以非常方便的實現這種協議:

class MyReceiveFilter : FixedHeaderReceiveFilter<BinaryRequestInfo>{ public MyReceiveFilter()  : base(6) { } protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length) {  return (int)header[offset + 4] * 256 + (int)header[offset + 5]; } protected override BinaryRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length) {  return new BinaryRequestInfo(Encoding.UTF8.GetString(header.Array, header.Offset, 4), bodyBuffer.CloneRange(offset, length)); }}

你需要基于類FixedHeaderReceiveFilter實現你自己的接收過濾器.

  • 傳入父類構造函數的 6 表示頭部的長度;
  • 方法"GetBodyLengthFromHeader(...)" 應該根據接收到的頭部返回請求體的長度;
  • 方法 ResolveRequestInfo(....)" 應該根據你接收到的請求頭部和請求體返回你的請求類型的實例.

實際使用場景:

到這里五種協議的模板你都已經了解了一遍,并且知道了相關的格式處理。接下來看一個網絡示例:

通訊協議格式:

在看到上圖協議是在糾結客戶端發送16進制,服務器怎么接收,16進制的報文如下:

26 01 00 19 4E 4A 30 31 31 01 44 41 31 31 32 00 07 00 00 00 00 00 00 34 23

16進制也好,10進制也好,其他的進制也好,最終都是轉換成byte[],其實在處理數據時,發送過去的數據都是可以轉換成為byte[]的,所以服務的只要解析byte[]數組就行了。按照協議來解析就能得到想要的數據。下面使用FixedSizeReceiveFilter的例子,代碼如下:

根據上面的通訊協議,開始來實現解析:

第一步、定義一個和協議合適的數據結構

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;/***************************************************************** 作者:黃昏前黎明后* CLR版本:4.0.30319.42000* 創建時間:2017-01-23 21:12:30* 2017* 描述說明:協議數據包** 修改歷史:*******************************************************************/namespace SuperSocketDemo{ public class HLData {  /// <summary>  /// 開始符號  /// </summary>  public char Head { get; set; }  /// <summary>  /// 協議包數據  /// </summary>  public byte Ping { get; set; }  /// <summary>  /// 數據長度  /// </summary>  public ushort Lenght { get; set; }  /// <summary>  /// 終端ID  /// </summary>  public uint FID { get; set; }  /// <summary>  /// 目標類型  /// </summary>  public byte Type { get; set; }  /// <summary>  /// 轉發終端ID  /// </summary>  public uint SID { get; set; }  /// <summary>  /// 發送計數  /// </summary>  public ushort SendCount { get; set; }  /// <summary>  /// 保留字段  /// </summary>  public byte[] Retain { get; set; }  /// <summary>  /// 異或校驗  /// </summary>  public byte Check { get; set; }  /// <summary>  /// 結束符號  /// </summary>  public char End { get; set; }  public override string ToString()  {   return string.Format("開始符號:{0},包數據:{1},數據長度:{2},終端ID:{3},目標類型:{4},轉發終端ID:{5},發送包計數:{6},保留字段:{7},異或校驗:{8},結束符號:{9}",    Head, Ping, Lenght, FID, Type, SID, SendCount, Retain, Check, End);  } }}HLData

第二步、建立一個RequestInfo來給server數據接收

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using SuperSocket.SocketBase.Protocol;/***************************************************************** 作者:黃昏前黎明后* CLR版本:4.0.30319.42000* 創建時間:2017-01-22 21:03:31* 2017* 描述說明:數據請求** 修改歷史:*******************************************************************/namespace SuperSocketDemo{ public class HLProtocolRequestInfo : RequestInfo<HLData> {  public HLProtocolRequestInfo(HLData hlData)  {   //如果需要使用命令行協議的話,那么命令類名稱HLData相同   Initialize("HLData", hlData);  } }}HLProtocolRequestInfo 類

第三步、FixedSize協議解析

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using SuperSocket.SocketBase.Protocol;using SuperSocket.Facility.Protocol;using SuperSocket.Common;/***************************************************************** 作者:黃昏前黎明后* CLR版本:4.0.30319.42000* 創建時間:2017-01-22 21:06:01* 2017* 描述說明:協議解析類,固定請求大小的協議** 修改歷史:*******************************************************************/namespace SuperSocketDemo{ /// <summary> /// 固定請求大小的協議,(幀格式為HLProtocolRequestInfo) /// </summary> public class HLProtocolReceiveFilter : FixedSizeReceiveFilter<HLProtocolRequestInfo> {  public HLProtocolReceiveFilter() : base(25)//總的字節長度 1+1+2+5+1+5+2+6+1+1 = 25  {  }  protected override HLProtocolRequestInfo ProcessMatchedRequest(byte[] buffer, int offset, int length, bool toBeCopied)  {   var HLData = new HLData();   HLData.Head = (char)buffer[offset];//開始標識的解析,1個字節   HLData.Ping = buffer[offset + 1];//數據,從第2位起,只有1個字節   HLData.Lenght = BitConverter.ToUInt16(buffer, offset + 2);//數據長度,從第3位開始,2個字節   HLData.FID = BitConverter.ToUInt32(buffer, offset + 4);//本終端ID,從第5位開始,5個字節   HLData.Type = buffer[offset + 9];//目標類型,從第10位開始,1個字節   HLData.SID = BitConverter.ToUInt32(buffer, offset + 10);//轉發終端ID,從第11位開始,5個字節   HLData.SendCount = BitConverter.ToUInt16(buffer, offset + 15);//發送包計數,從第16位開始,2個字節   HLData.Retain = buffer.CloneRange(offset + 17, 6);//保留字段,從18位開始,6個字節   HLData.Check = buffer[offset + 23];//異或校驗,從24位開始,1個字節   HLData.End = (char)buffer[offset + 24];//結束符號,從第25位開始,一個字節   return new HLProtocolRequestInfo(HLData);  } }}HLProtocolReceiveFilter類

第四步、建立協議工廠HLReceiveFilterFactory

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using SuperSocket.SocketBase;using SuperSocket.SocketBase.Protocol;using System.Net;/***************************************************************** 作者:黃昏前黎明后* CLR版本:4.0.30319.42000* 創建時間:2017-01-23 :22:01:25* 2017* 描述說明:協議工廠** 修改歷史:*******************************************************************/namespace SuperSocketDemo{ public class HLReceiveFilterFactory: IReceiveFilterFactory<HLProtocolRequestInfo> {  public IReceiveFilter<HLProtocolRequestInfo> CreateFilter(IAppServer appServer, IAppSession appSession, IPEndPoint remoteEndPoint)  {   return new HLBeginEndMarkReceiveFilter();  } }}HLReceiveFilterFactory類

第五步、自定義HLProtocolSession繼承AppSession

using SuperSocket.SocketBase;using SuperSocket.SocketBase.Protocol;using System;/***************************************************************** 作者:黃昏前黎明后* CLR版本:4.0.30319.42000* 創建時間:2017-01-22 21:15:11* 2017* 描述說明:自定義HLProtocolSession** 修改歷史:*******************************************************************/namespace SuperSocketDemo{ public class HLProtocolSession : AppSession<HLProtocolSession, HLProtocolRequestInfo> {  protected override void HandleException(Exception e)  {  } }}HLProtocolSession類

第六步、自定義HLProtocolServer繼承AppServer

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using SuperSocket.SocketBase;using SuperSocket.SocketBase.Protocol;/*****************************************************************  作者:黃昏前黎明后*  CLR版本:4.0.30319.42000*  創建時間:2017-01-22 21:16:57*  2017*  描述說明:自定義server**  修改歷史:*******************************************************************/namespace SuperSocketDemo{ public class HLProtocolServer : AppServer<HLProtocolSession, HLProtocolRequestInfo>  {    /// <summary>    /// 使用自定義協議工廠    /// </summary>    public HLProtocolServer()      : base(new HLReceiveFilterFactory())     {    }  }}HLProtocolServer類

第七步、加上起止符協議HLBeginEndMarkReceiveFilter

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using SuperSocket.Common;using SuperSocket.Facility.Protocol;/*****************************************************************  作者:黃昏前黎明后*  CLR版本:4.0.30319.42000*  創建時間:2017-01-23 22:07:03*  2017*  描述說明:帶起止符的協議, "&" 是開始標記, "#" 是結束標記,開始結束標記由自己定義**  修改歷史:*******************************************************************/namespace SuperSocketDemo{  public class HLBeginEndMarkReceiveFilter : BeginEndMarkReceiveFilter<HLProtocolRequestInfo>  {    private readonly static char strBegin = '&';    private readonly static char strEnd = '#';    //開始和結束標記也可以是兩個或兩個以上的字節    private readonly static byte[] BeginMark = new byte[] { (byte)strBegin };    private readonly static byte[] EndMark = new byte[] { (byte)strEnd };    public HLBeginEndMarkReceiveFilter() : base(BeginMark, EndMark)    {    }    /// <summary>    /// 這里解析的到的數據是會把頭和尾部都給去掉的    /// </summary>    /// <param name="readBuffer"></param>    /// <param name="offset"></param>    /// <param name="length"></param>    /// <returns></returns>    protected override HLProtocolRequestInfo ProcessMatchedRequest(byte[] readBuffer, int offset, int length)    {      var HLData = new HLData();      HLData.Head = strBegin;//自己定義開始符號      HLData.Ping = readBuffer[offset];//數據,從第1位起,只有1個字節      HLData.Lenght = BitConverter.ToUInt16(readBuffer, offset + 1);//數據長度,從第2位開始,2個字節      HLData.FID = BitConverter.ToUInt32(readBuffer, offset + 3);//本終端ID,從第4位開始,5個字節      HLData.Type = readBuffer[offset + 8];//目標類型,從第9位開始,1個字節      HLData.SID = BitConverter.ToUInt32(readBuffer, offset + 9);//轉發終端ID,從第10位開始,5個字節      HLData.SendCount = BitConverter.ToUInt16(readBuffer, offset + 14);//發送包計數,從第15位開始,2個字節      HLData.Retain = readBuffer.CloneRange(offset + 16, 6);//保留字段,從17位開始,6個字節      HLData.Check = readBuffer[offset + 22];//異或校驗,從23位開始,1個字節      HLData.End = strEnd;//結束符號,自己定義      return new HLProtocolRequestInfo(HLData);    }  }}HLBeginEndMarkReceiveFilter類

第八步、服務啟動和停止

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using SuperSocket.SocketBase;using SuperSocket.SocketBase.Protocol;using SuperSocket.SocketEngine;/*****************************************************************  作者:黃昏前黎明后*  CLR版本:4.0.30319.42000*  創建時間:2017-01-19 00:02:17*  2017*  描述說明:服務啟動和停止入口 **  修改歷史: 2017 -01-19 調整自定義mysession和myserver*       2017 -01-23 通訊協議解析,直接使用入口注冊事件******************************************************************/namespace SuperSocketDemo{  class Program  {    /// <summary>    /// SuperSocket服務啟動或停止    /// </summary>    /// <param name="args"></param>    static void Main(string[] args)    {      Console.WriteLine("請按任何鍵進行啟動SuperSocket服務!");      Console.ReadKey();      Console.WriteLine();      var HLProtocolServer = new HLProtocolServer();      // 設置端口號      int port = 2017;      //啟動應用服務端口      if (!HLProtocolServer.Setup(port)) //啟動時監聽端口2017      {        Console.WriteLine("服務端口啟動失敗!");        Console.ReadKey();        return;      }      Console.WriteLine();      //注冊連接事件      HLProtocolServer.NewSessionConnected += HLProtocolServer_NewSessionConnected;      //注冊請求事件      HLProtocolServer.NewRequestReceived += HLProtocolServer_NewRequestReceived;      //注冊Session關閉事件      HLProtocolServer.SessionClosed += HLProtocolServer_SessionClosed;      //嘗試啟動應用服務      if (!HLProtocolServer.Start())      {        Console.WriteLine("服務啟動失敗!");        Console.ReadKey();        return;      }      Console.WriteLine("服務器狀態:" + HLProtocolServer.State.ToString());      Console.WriteLine("服務啟動成功,請按'E'停止服務!");      while (Console.ReadKey().KeyChar != 'E')      {        Console.WriteLine();        continue;      }      //停止服務      HLProtocolServer.Stop();      Console.WriteLine("服務已停止!");      Console.ReadKey();    }    static void HLProtocolServer_SessionClosed(HLProtocolSession session, SuperSocket.SocketBase.CloseReason value)    {      Console.WriteLine(session.RemoteEndPoint.ToString() + "連接斷開. 斷開原因:" + value);    }    static void HLProtocolServer_NewSessionConnected(HLProtocolSession session)    {      Console.WriteLine(session.RemoteEndPoint.ToString() + " 已連接.");    }    /// <summary>    /// 協議并沒有什么太多復雜邏輯,不需要用到命令模式,直接用這種方式就可以了    /// </summary>    /// <param name="session"></param>    /// <param name="requestInfo"></param>    private static void HLProtocolServer_NewRequestReceived(HLProtocolSession session, HLProtocolRequestInfo requestInfo)    {      Console.WriteLine();      Console.WriteLine("數據來源: " + session.RemoteEndPoint.ToString());      Console.WriteLine("接收數據內容:"+requestInfo.Body);    }  }}Program類

通訊協議需要使用小工具進行調試,本人使用的是TCP/UDP端口調試工具SocketTool V2.大家可以直接進行下載。使用HEX模式進行發送16進制報文,服務器輸出結果:

本文參考官方文檔 內置的常用協議實現模版

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持武林網!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 方山县| 平阳县| 鄢陵县| 佛冈县| 静安区| 洪江市| 甘孜县| 阿鲁科尔沁旗| 青神县| 新安县| 太保市| 泸西县| 富裕县| 明溪县| 旺苍县| 鹤峰县| 深圳市| 余庆县| 三都| 吐鲁番市| 河西区| 水富县| 峡江县| 张家港市| 拉孜县| 太仓市| 静海县| 黎城县| 祥云县| 桃园县| 岢岚县| 灌云县| 大田县| 望都县| 图片| 昆明市| 当阳市| 高碑店市| 增城市| 宣化县| 吉安市|