欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2017年01月24日 09:08:20   作者:黃昏前黎明后  
本文主要介紹了常用協(xié)議實現(xiàn)模版及FixedSizeReceiveFilter示例。具有很好的參考價值,下面跟著小編一起來看下吧

Socket里面的協(xié)議解析是Socket通訊程序設(shè)計中最復(fù)雜的地方,如果你的應(yīng)用層協(xié)議設(shè)計或?qū)崿F(xiàn)不佳,Socket通訊中常見的粘包,分包就難以避免。SuperSocket內(nèi)置了命令行格式的協(xié)議CommandLineProtocol,如果你使用了其它格式的協(xié)議,就必須自行實現(xiàn)自定義協(xié)議CustomProtocol??戳艘黄臋n之后, 你可能會覺得用 SuperSocket 來實現(xiàn)你的自定義協(xié)議并不簡單。 為了讓這件事變得更容易一些, SuperSocket 提供了一些通用的協(xié)議解析工具, 你可以用他們簡單而且快速的實現(xiàn)你自己的通信協(xié)議:

  • TerminatorReceiveFilter (SuperSocket.SocketBase.Protocol.TerminatorReceiveFilter, SuperSocket.SocketBase) ---結(jié)束符協(xié)議
  • CountSpliterReceiveFilter (SuperSocket.Facility.Protocol.CountSpliterReceiveFilter, SuperSocket.Facility)---固定數(shù)量分隔符協(xié)議
  • FixedSizeReceiveFilter (SuperSocket.Facility.Protocol.FixedSizeReceiveFilter, SuperSocket.Facility)---固定請求大小協(xié)議
  • BeginEndMarkReceiveFilter (SuperSocket.Facility.Protocol.BeginEndMarkReceiveFilter, SuperSocket.Facility)---帶起止符協(xié)議
  • FixedHeaderReceiveFilter (SuperSocket.Facility.Protocol.FixedHeaderReceiveFilter, SuperSocket.Facility)---頭部格式固定并包含內(nèi)容長度協(xié)議

1、TerminatorReceiveFilter結(jié)束符協(xié)議

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

結(jié)束符協(xié)議TerminatorProtocolServer :

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

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

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

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

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

2、CountSpliterReceiveFilter 固定數(shù)量分隔符協(xié)議

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

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

3、FixedSizeReceiveFilter 固定請求大小協(xié)議

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

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

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

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

4、BeginEndMarkReceiveFilter 帶起止符協(xié)議

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

class MyReceiveFilter : BeginEndMarkReceiveFilter<StringRequestInfo>
{
 //開始和結(jié)束標(biāo)記也可以是兩個或兩個以上的字節(jié)
 private readonly static byte[] BeginMark = new byte[] { (byte)'&' };
 private readonly static byte[] EndMark = new byte[] { (byte)'#' };

 public MyReceiveFilter()
  : base(BeginMark, EndMark) //傳入開始標(biāo)記和結(jié)束標(biāo)記
 {
 }
 protected override StringRequestInfo ProcessMatchedRequest(byte[] readBuffer, int offset, int length)
 {
  //TODO: 通過解析到的數(shù)據(jù)來構(gòu)造請求實例,并返回
 }
}

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

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

5、FixedHeaderReceiveFilter 頭部格式固定并包含內(nèi)容長度協(xié)議

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

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

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

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實現(xiàn)你自己的接收過濾器.

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

實際使用場景:

到這里五種協(xié)議的模板你都已經(jīng)了解了一遍,并且知道了相關(guān)的格式處理。接下來看一個網(wǎng)絡(luò)示例:

通訊協(xié)議格式:

在看到上圖協(xié)議是在糾結(jié)客戶端發(fā)送16進制,服務(wù)器怎么接收,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進制也好,其他的進制也好,最終都是轉(zhuǎn)換成byte[],其實在處理數(shù)據(jù)時,發(fā)送過去的數(shù)據(jù)都是可以轉(zhuǎn)換成為byte[]的,所以服務(wù)的只要解析byte[]數(shù)組就行了。按照協(xié)議來解析就能得到想要的數(shù)據(jù)。下面使用FixedSizeReceiveFilter的例子,代碼如下:

根據(jù)上面的通訊協(xié)議,開始來實現(xiàn)解析:

第一步、定義一個和協(xié)議合適的數(shù)據(jù)結(jié)構(gòu)

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

第二步、建立一個RequestInfo來給server數(shù)據(jù)接收

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
* 創(chuàng)建時間:2017-01-22 21:03:31
* 2017
* 描述說明:數(shù)據(jù)請求
*
* 修改歷史:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
 public class HLProtocolRequestInfo : RequestInfo<HLData>
 {
  public HLProtocolRequestInfo(HLData hlData)
  {
   //如果需要使用命令行協(xié)議的話,那么命令類名稱HLData相同
   Initialize("HLData", hlData);
  }
 }
}
HLProtocolRequestInfo 類

第三步、FixedSize協(xié)議解析

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
* 創(chuàng)建時間:2017-01-22 21:06:01
* 2017
* 描述說明:協(xié)議解析類,固定請求大小的協(xié)議
*
* 修改歷史:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
 /// <summary>
 /// 固定請求大小的協(xié)議,(幀格式為HLProtocolRequestInfo)
 /// </summary>
 public class HLProtocolReceiveFilter : FixedSizeReceiveFilter<HLProtocolRequestInfo>
 {
  public HLProtocolReceiveFilter() : base(25)//總的字節(jié)長度 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];//開始標(biāo)識的解析,1個字節(jié)
   HLData.Ping = buffer[offset + 1];//數(shù)據(jù),從第2位起,只有1個字節(jié)
   HLData.Lenght = BitConverter.ToUInt16(buffer, offset + 2);//數(shù)據(jù)長度,從第3位開始,2個字節(jié)
   HLData.FID = BitConverter.ToUInt32(buffer, offset + 4);//本終端ID,從第5位開始,5個字節(jié)
   HLData.Type = buffer[offset + 9];//目標(biāo)類型,從第10位開始,1個字節(jié)
   HLData.SID = BitConverter.ToUInt32(buffer, offset + 10);//轉(zhuǎn)發(fā)終端ID,從第11位開始,5個字節(jié)
   HLData.SendCount = BitConverter.ToUInt16(buffer, offset + 15);//發(fā)送包計數(shù),從第16位開始,2個字節(jié)
   HLData.Retain = buffer.CloneRange(offset + 17, 6);//保留字段,從18位開始,6個字節(jié)
   HLData.Check = buffer[offset + 23];//異或校驗,從24位開始,1個字節(jié)
   HLData.End = (char)buffer[offset + 24];//結(jié)束符號,從第25位開始,一個字節(jié)
   return new HLProtocolRequestInfo(HLData);
  }
 }
}
HLProtocolReceiveFilter類

第四步、建立協(xié)議工廠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
* 創(chuàng)建時間:2017-01-23 :22:01:25
* 2017
* 描述說明:協(xié)議工廠
*
* 修改歷史:
*
*
*****************************************************************/
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
* 創(chuàng)建時間: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
*  創(chuàng)建時間:2017-01-22 21:16:57
*  2017
*  描述說明:自定義server
*
*  修改歷史:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
 public class HLProtocolServer : AppServer<HLProtocolSession, HLProtocolRequestInfo>
  {
    /// <summary>
    /// 使用自定義協(xié)議工廠
    /// </summary>
    public HLProtocolServer()
      : base(new HLReceiveFilterFactory()) 
    {
    }
  }
}

HLProtocolServer類

第七步、加上起止符協(xié)議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
*  創(chuàng)建時間:2017-01-23 22:07:03
*  2017
*  描述說明:帶起止符的協(xié)議, "&" 是開始標(biāo)記, "#" 是結(jié)束標(biāo)記,開始結(jié)束標(biāo)記由自己定義
*
*  修改歷史:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
  public class HLBeginEndMarkReceiveFilter : BeginEndMarkReceiveFilter<HLProtocolRequestInfo>
  {
    private readonly static char strBegin = '&';
    private readonly static char strEnd = '#';
    //開始和結(jié)束標(biāo)記也可以是兩個或兩個以上的字節(jié)
    private readonly static byte[] BeginMark = new byte[] { (byte)strBegin };
    private readonly static byte[] EndMark = new byte[] { (byte)strEnd };

    public HLBeginEndMarkReceiveFilter() : base(BeginMark, EndMark)
    {
    }
    /// <summary>
    /// 這里解析的到的數(shù)據(jù)是會把頭和尾部都給去掉的
    /// </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];//數(shù)據(jù),從第1位起,只有1個字節(jié)
      HLData.Lenght = BitConverter.ToUInt16(readBuffer, offset + 1);//數(shù)據(jù)長度,從第2位開始,2個字節(jié)
      HLData.FID = BitConverter.ToUInt32(readBuffer, offset + 3);//本終端ID,從第4位開始,5個字節(jié)
      HLData.Type = readBuffer[offset + 8];//目標(biāo)類型,從第9位開始,1個字節(jié)
      HLData.SID = BitConverter.ToUInt32(readBuffer, offset + 9);//轉(zhuǎn)發(fā)終端ID,從第10位開始,5個字節(jié)
      HLData.SendCount = BitConverter.ToUInt16(readBuffer, offset + 14);//發(fā)送包計數(shù),從第15位開始,2個字節(jié)
      HLData.Retain = readBuffer.CloneRange(offset + 16, 6);//保留字段,從17位開始,6個字節(jié)
      HLData.Check = readBuffer[offset + 22];//異或校驗,從23位開始,1個字節(jié)
      HLData.End = strEnd;//結(jié)束符號,自己定義
      return new HLProtocolRequestInfo(HLData);
    }
  }
}

HLBeginEndMarkReceiveFilter類

第八步、服務(wù)啟動和停止

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
*  創(chuàng)建時間:2017-01-19 00:02:17
*  2017
*  描述說明:服務(wù)啟動和停止入口 
*
*  修改歷史: 2017 -01-19 調(diào)整自定義mysession和myserver
*       2017 -01-23 通訊協(xié)議解析,直接使用入口注冊事件
*
*****************************************************************/
namespace SuperSocketDemo
{
  class Program
  {
    /// <summary>
    /// SuperSocket服務(wù)啟動或停止
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
      Console.WriteLine("請按任何鍵進行啟動SuperSocket服務(wù)!");
      Console.ReadKey();
      Console.WriteLine();
      var HLProtocolServer = new HLProtocolServer();
      // 設(shè)置端口號
      int port = 2017;
      //啟動應(yīng)用服務(wù)端口
      if (!HLProtocolServer.Setup(port)) //啟動時監(jiān)聽端口2017
      {
        Console.WriteLine("服務(wù)端口啟動失敗!");
        Console.ReadKey();
        return;
      }
      Console.WriteLine();
      //注冊連接事件
      HLProtocolServer.NewSessionConnected += HLProtocolServer_NewSessionConnected;
      //注冊請求事件
      HLProtocolServer.NewRequestReceived += HLProtocolServer_NewRequestReceived;
      //注冊Session關(guān)閉事件
      HLProtocolServer.SessionClosed += HLProtocolServer_SessionClosed;
      //嘗試啟動應(yīng)用服務(wù)
      if (!HLProtocolServer.Start())
      {
        Console.WriteLine("服務(wù)啟動失敗!");
        Console.ReadKey();
        return;
      }
      Console.WriteLine("服務(wù)器狀態(tài):" + HLProtocolServer.State.ToString());
      Console.WriteLine("服務(wù)啟動成功,請按'E'停止服務(wù)!");
      while (Console.ReadKey().KeyChar != 'E')
      {
        Console.WriteLine();
        continue;
      }
      //停止服務(wù)
      HLProtocolServer.Stop();
      Console.WriteLine("服務(wù)已停止!");
      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>
    /// 協(xié)議并沒有什么太多復(fù)雜邏輯,不需要用到命令模式,直接用這種方式就可以了
    /// </summary>
    /// <param name="session"></param>
    /// <param name="requestInfo"></param>
    private static void HLProtocolServer_NewRequestReceived(HLProtocolSession session, HLProtocolRequestInfo requestInfo)
    {
      Console.WriteLine();
      Console.WriteLine("數(shù)據(jù)來源: " + session.RemoteEndPoint.ToString());
      Console.WriteLine("接收數(shù)據(jù)內(nèi)容:"+requestInfo.Body);
    }
  }
}

Program類

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

本文參考官方文檔 內(nèi)置的常用協(xié)議實現(xiàn)模版

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

最新評論