Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Einführung in C#-Common-Protocol-Implementierungsvorlagen und Beispielcode von FixedSizeReceiveFilter

Detaillierte Einführung in C#-Common-Protocol-Implementierungsvorlagen und Beispielcode von FixedSizeReceiveFilter

黄舟
黄舟Original
2017-03-27 11:16:112866Durchsuche

In diesem Artikel werden hauptsächlich allgemeine Protokollimplementierungsvorlagen und FixedSizeReceiveFilter-Beispiele vorgestellt. Es hat einen sehr guten Referenzwert, schauen wir es uns mit dem Editor unten an

Die Protokollanalyse in Socket ist der komplizierteste Teil der Socket-Kommunikationsprogrammierung. Wenn Ihr Anwendungsschichtprotokoll schlecht entworfen oder implementiert ist, Sticky-Pakete kommen in der Socket-Kommunikation häufig vor und eine Unterverpackung ist unvermeidbar. SuperSocket verfügt über ein integriertes Befehlszeilenformatprotokoll CommandLineProtocol. Wenn Sie ein Protokoll in anderen Formaten verwenden, müssen Sie das benutzerdefinierte Protokoll CustomProtocol selbst implementieren. Nachdem Sie ein Dokument gelesen haben, haben Sie vielleicht das Gefühl, dass es nicht einfach ist, Ihr benutzerdefiniertes Protokoll mit SuperSocket zu implementieren. Um dies zu vereinfachen, stellt SuperSocket einige allgemeine Protokoll-Parsing-Tools bereit, mit denen Sie einfach und schnell Ihr eigenes Kommunikationsprotokoll implementieren können:

  • TerminatorReceiveFilter (SuperSocket.SocketBase .Protocol.TerminatorReceiveFilter, SuperSocket.SocketBase) --- Terminator Protocol

  • CountSpliterReceiveFilter (SuperSocket.Facility.Protocol.CountSpliterReceiveFilter , SuperSocket.Facility)---Behoben Zahlentrennzeichenprotokoll

  • FixedSizeReceiveFilter (SuperSocket.Facility.Protocol.FixedSizeReceiveFilter, SuperSocket.Facility)---Festes Anforderungsgrößenprotokoll

  • BeginEndMarkReceiveFilter (SuperSocket.Facility.Protocol.BeginEndMarkReceiveFilter, SuperSocket.Facility)---Protokoll mit Start- und Endzeichen

  • FixedHeaderReceiveFilter (SuperSocket.Facility.Protocol.FixedHeaderReceiveFilter, SuperSocket.Facility)---Das Header-Format ist fest und enthält das Inhaltslängenprotokoll

1. TerminatorReceiveFilter-Terminatorprotokoll

Das Terminator-Protokoll ähnelt dem Befehlszeilenprotokoll. Einige Protokolle verwenden Terminatoren, um eine Anfrage zu bestimmen. Beispielsweise verwendet ein Protokoll zwei Zeichen „##“ als Terminator. Sie können also die Klasse verwenden „TerminatorReceiveFilterFactory“:

Terminator Protocol TerminatorProtocolServer:

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

zur Implementierung Ihres Empfangsfilters (ReceiveFilter): basierend auf TerminatorReceiveFilter

public class YourReceiveFilter : TerminatorReceiveFilter<YourRequestInfo>
{
 //More code
}
Implementieren Sie Ihre Empfangsfilterfabrik (ReceiveFilterFactory), um Akzeptanzfilterinstanzen zu erstellen:

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

2. CountSpliterReceiveFilter-Protokoll mit festem Zahlentrennzeichen

Einige Protokolle definieren Anforderungen im Format „ #part1#part2#part3#part4#part5#part6#part7#". Jede Anfrage besteht aus 7 Teilen, die durch „#“ getrennt sind. Die Implementierung dieses Protokolls ist sehr einfach:

/// <summary>
/// 请求格式:#part1#part2#part3#part4#part5#part6#part7#
/// </summary>
public class CountSpliterAppServer : AppServer
{
 public CountSpliterAppServer()
  : base(new CountSpliterReceiveFilterFactory((byte)&#39;#&#39;, 8)) //8个分隔符,7个参数。除使用默认的过滤工厂,还可以参照上一个实例定制协议
 {
 }
}

3 . FixedSizeReceiveFilter Protokoll mit fester Anforderungsgröße

In diesem Protokoll ist die Größe aller Anforderungen gleich. Wenn jede Ihrer Anfragen eine 8-stellige

-Zeichenfolge ist, wie zum Beispiel „HUANG LI“, sollten Sie einen Empfangsfilter (ReceiveFilter) mit dem folgenden Code implementieren:

class MyReceiveFilter : FixedSizeReceiveFilter<StringRequestInfo>
{
 public MyReceiveFilter()
  : base(8) //传入固定的请求大小
 {
 }
 protected override StringRequestInfo ProcessMatchedRequest(byte[] buffer, int offset, int length, bool toBeCopied)
 {
  //TODO: 通过解析到的数据来构造请求实例,并返回
 }
}
Dann verwenden Sie diesen Akzeptanzfilter (ReceiveFilter) in Ihrer AppServer-Klasse:

public class MyAppServer : AppServer
{
 public MyAppServer()
  : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //使用默认的接受过滤器工厂 (DefaultReceiveFilterFactory)
 {
 }
}

4. BeginEndMarkReceiveFilter mit Start- und Endzeichenprotokoll

Es gibt feste Start- und Endmarkierungen in jeder Anfrage dieses Protokolltyps. Ich habe zum Beispiel ein Protokoll, bei dem alle Nachrichten dem Format „&xxxxxxxxxxxxxx#“ folgen. In diesem Fall ist „&“ also das öffnende Tag und „#“ das schließende Tag, sodass Ihr Akzeptanzfilter wie folgt definiert werden kann:

class MyReceiveFilter : BeginEndMarkReceiveFilter<StringRequestInfo>
{
 //开始和结束标记也可以是两个或两个以上的字节
 private readonly static byte[] BeginMark = new byte[] { (byte)&#39;&&#39; };
 private readonly static byte[] EndMark = new byte[] { (byte)&#39;#&#39; };

 public MyReceiveFilter()
  : base(BeginMark, EndMark) //传入开始标记和结束标记
 {
 }
 protected override StringRequestInfo ProcessMatchedRequest(byte[] readBuffer, int offset, int length)
 {
  //TODO: 通过解析到的数据来构造请求实例,并返回
 }
}
und dann in Ihrer AppServer-Klasse „Dieses Akzeptieren“ verwendet werden kann Filter (ReceiveFilter):

public class MyAppServer : AppServer
{
 public MyAppServer()
  : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //使用默认的接受过滤器工厂 (DefaultReceiveFilterFactory)
 {
 }
}

5. Das FixedHeaderReceiveFilter-Headerformat ist fest und enthält ein Inhaltslängenprotokoll

Dieses Protokoll definiert eine Anfrage als zwei Meistens den ersten Teil definiert grundlegende Informationen, einschließlich der Länge des zweiten Teils usw. Wir nennen den ersten Teil normalerweise den Header.

Zum Beispiel haben wir ein Protokoll wie dieses: Der Header enthält 6 Bytes, bevor 4 Bytes verwendet werden um den Namen der Anfrage zu speichern, und die letzten beiden Bytes werden verwendet, um die Länge des Anfragetextes darzustellen:

/// +-------+---+-------------------------------+
/// |request| l |                               |
/// | name  | e |    request body               |
/// |  (4)  | n |                               |
/// |       |(2)|                               |
/// +-------+---+-------------------------------+
Mit SuperSocket können Sie dieses Protokoll sehr bequem implementieren:

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));
 }
}
Sie müssen Ihren eigenen Empfangsfilter basierend auf der Klasse FixedHeaderReceiveFilter implementieren.

Tatsächliche Nutzung Szenarien:

Sie haben die Vorlagen der fünf Protokolle bereits verstanden und kennen die entsprechende Formatverarbeitung. Schauen wir uns als Nächstes ein Netzwerkbeispiel an:

Kommunikationsprotokollformat:

在看到上图协议是在纠结客户端发送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 = &#39;&&#39;;
    private readonly static char strEnd = &#39;#&#39;;
    //开始和结束标记也可以是两个或两个以上的字节
    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("服务启动成功,请按&#39;E&#39;停止服务!");
      while (Console.ReadKey().KeyChar != &#39;E&#39;)
      {
        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进制报文,服务器输出结果:

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in C#-Common-Protocol-Implementierungsvorlagen und Beispielcode von FixedSizeReceiveFilter. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn