Home  >  Article  >  Backend Development  >  Detailed introduction to C# common protocol implementation templates and sample code of FixedSizeReceiveFilter

Detailed introduction to C# common protocol implementation templates and sample code of FixedSizeReceiveFilter

黄舟
黄舟Original
2017-03-27 11:16:112867browse

This article mainly introduces common protocol implementation templates and FixedSizeReceiveFilter examples. It has a very good reference value. Let’s take a look at it with the editor.

The protocol analysis in Socket is the most complicated part of Socket communication programming. If your application layer protocol is poorly designed or implemented, Sticky packets are common in Socket communication, and sub-packaging is unavoidable. SuperSocket has a built-in command line format protocol CommandLineProtocol. If you use a protocol in other formats, you must implement the custom protocol CustomProtocol yourself. After reading a document, you may feel that it is not simple to implement your custom protocol with SuperSocket. To make this easier, SuperSocket provides some general protocol parsing tools, you can use them to implement your own communication protocol simply and quickly:

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

  • ##CountSpliterReceiveFilter (SuperSocket.Facility.Protocol.CountSpliterReceiveFilter , SuperSocket.Facility)---Fixed number delimiter protocol

  • FixedSizeReceiveFilter (SuperSocket.Facility.Protocol.FixedSizeReceiveFilter, SuperSocket.Facility)---Fixed request Size protocol

  • BeginEndMarkReceiveFilter (SuperSocket.Facility.Protocol.BeginEndMarkReceiveFilter, SuperSocket.Facility)---Protocol with start and end characters

  • FixedHeaderReceiveFilter (SuperSocket.Facility.Protocol.FixedHeaderReceiveFilter, SuperSocket.Facility)---Header format is fixed and contains content length protocol

1. TerminatorReceiveFilter terminator protocol

The terminator protocol is similar to the command line protocol. Some protocols use terminators to determine a request. For example, a protocol uses two characters "##" as the terminator, So you can use the class "TerminatorReceiveFilterFactory":

Terminator Protocol TerminatorProtocolServer:

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

Implement your reception based on TerminatorReceiveFilter

Filter(ReceiveFilter):

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

Implement your receive filter factory (ReceiveFilterFactory) for creating accept filter instances:

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

2, CountSpliterReceiveFilter fixed number separator protocol

Some protocol definitions A request in the format "#part1#part2#part3#part4#part5#part6#part7#". Each request has 7 parts separated by '#'. The implementation of this protocol is very simple:

/// <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 fixed request size protocol

In this protocol, the size of all requests is the same. If each of your requests is a

string consisting of 8 characters, such as "HUANG LI", what you should do is to implement a receive filter (ReceiveFilter) with the following code:

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

Then use this acceptance filter (ReceiveFilter) in your AppServer class:

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

4. BeginEndMarkReceiveFilter with start and end character protocol

In this type Each request in the protocol has fixed start and end tags. For example, I have a protocol where all messages follow the format "&xxxxxxxxxxxxxx#". So, in this case, "&" is the opening tag and "#" is the closing tag, so your accept filter can be defined like this:

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: 通过解析到的数据来构造请求实例,并返回
 }
}

and then use this accept in your AppServer class Filter (ReceiveFilter):

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

5. FixedHeaderReceiveFilter header format is fixed and contains content length protocol

This protocol defines a request as two parts, the first One part defines basic information including the length of the second part and so on. We usually call the first part the header.

For example, we have a protocol like this: the header contains 6 bytes, the first 4 bytes Used to store the name of the request, and the last two bytes are used to represent the length of the request body:

/// +-------+---+-------------------------------+
/// |request| l |                               |
/// | name  | e |    request body               |
/// |  (4)  | n |                               |
/// |       |(2)|                               |
/// +-------+---+-------------------------------+

Using SuperSocket, you can implement this protocol very conveniently:

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));
 }
}

You need to be based on Class FixedHeaderReceiveFilter implements your own receive filter.

  • Pass in the parent class

    constructorThe 6 represents the length of the header;

  • The method "GetBodyLengthFromHeader(...)" should return the length of the request body according to the received header;

  • The method "ResolveRequestInfo(....)" should return the length of the request body according to your The received request header and request body return an instance of your

    request type.

Actual usage scenario:

Now you have understood the templates of the five protocols and know the relevant format processing. Next, let’s look at a network example:

Communication protocol format:

在看到上图协议是在纠结客户端发送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进制报文,服务器输出结果:

The above is the detailed content of Detailed introduction to C# common protocol implementation templates and sample code of FixedSizeReceiveFilter. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn