Introduction to SuperSocket (V) - Common Protocol Implementation Template and Fixed Size Receive Filter Example

Keywords: C# Session socket encoding network

Protocol parsing in Socket is the most complex part in the design of Socket communication program. If your application layer protocol is not well designed or implemented, the common sticky package in Socket communication will make subcontracting unavoidable. SuperSocket has built-in CommandLine Protocol in command line format. If you use protocols in other formats, you must implement Custom Protocol by yourself. After reading a document, you may find it not easy to implement your custom protocol with SuperSocket. To make this easier, SuperSocket provides some common protocol parsing tools that you can use to implement your own communication protocols simply and quickly:
  • Terminator Receive Filter (SuperSocket. SocketBase. Protocol. Terminator Receive Filter, SuperSocket. SocketBase) - - Terminator Protocol
  • CountSpliter Receive Filter (SuperSocket. Facility. Protocol. CountSpliter Receive Filter, SuperSocket. Facility) - - Fixed Number Separator Protocol
  • Fixed Size Receive Filter (SuperSocket. Facility. Protocol. Fixed Size Receive Filter, SuperSocket. Facility) - - Fixed Request Size Protocol
  • BeginEndMarkReceive Filter (SuperSocket. Facility. Protocol. BeginEndMarkReceive Filter, SuperSocket. Facility) - - With Stop Protocol
  • Fixed Header Receive Filter (SuperSocket. Facility. Protocol. Fixed Header Receive Filter, SuperSocket. Facility) -- Header format is fixed and contains content length protocol

Terminator Receive Filter Ender Protocol

The terminator protocol is similar to the command-line protocol in that some protocols use terminators to determine a request. For example, a protocol uses two characters "#" as the terminator, so you can use the class "Terminator Receive FilterFactory":

Terminator Protocol Server:

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

    }
}

Implement your Receive Filter based on Terminator Receive Filter:

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

Implement your Receive Filter Factory to create an instance of the Receive Filter:

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

CountSpliter Receive Filter Fixed Quantity Separator Protocol

Some protocols define requests in a format like this " Part1 part2 part3 Part4 Part5 part6 Part7 ". Each request has seven parts separated by''. The implementation of this protocol is very simple:

/// <summary>
/// Request format:  Part1 part2 part3 Part4 Part5 Part6 part7#
/// </summary>
public class CountSpliterAppServer : AppServer
{
    public CountSpliterAppServer()
        : base(new CountSpliterReceiveFilterFactory((byte)'#', 8)//8 delimiters, 7 parameters. In addition to using the default filter factory, you can customize the protocol with reference to the previous instance.
    {

    }
}

Fixed Size Receive Filter Fixed Request Size Protocol

In this protocol, all requests are of the same size. If each request is a string of eight characters, such as "HUANG LI", what you should do is to implement a Receive Filter as follows:

class MyReceiveFilter : FixedSizeReceiveFilter<StringRequestInfo>
{
    public MyReceiveFilter()
        : base(8) //Input fixed request size
    {

    }

    protected override StringRequestInfo ProcessMatchedRequest(byte[] buffer, int offset, int length, bool toBeCopied)
    {
        //TODO: Construct the request instance from the parsed data and return it
    }
}

Then use this Receive Filter in your AppServer class:

public class MyAppServer : AppServer
{
    public MyAppServer()
        : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //Use Default Receive Filter Factory
    {

    }
}

4. BeginEndMarkReceive Filter with Stop Protocol

There are fixed start and end tags in each request of such protocols. For ex amp le, I have a protocol in which all messages follow this format "& xxxxxxxxxxx#". Therefore, in this case, "&" is the start tag, "#" is the end tag, so your acceptance filter can be defined as as follows:__________

class MyReceiveFilter : BeginEndMarkReceiveFilter<StringRequestInfo>
{
    //Start and end tags can also be two or more bytes
    private readonly static byte[] BeginMark = new byte[] { (byte)'&' };
    private readonly static byte[] EndMark = new byte[] { (byte)'#' };

    public MyReceiveFilter()
        : base(BeginMark, EndMark) //Input start and end markers
    {

    }

    protected override StringRequestInfo ProcessMatchedRequest(byte[] readBuffer, int offset, int length)
    {
        //TODO: Construct the request instance from the parsed data and return it
    }
}

Then use this Receive Filter in your AppServer class:

public class MyAppServer : AppServer
{
    public MyAppServer()
        : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //Use Default Receive Filter Factory
    {

    }
}

5. Fixed Header Receive Filter Header Format Fixed and Contains Content Length Protocol

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

For example, we have a protocol where the header contains six bytes, the first four bytes are 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)|                               |
/// +-------+---+-------------------------------+

With SuperSocket, you can easily implement this protocol:

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 implement your own reception filter based on the class Fixed Header Receive Filter.

    • The length of the head is represented by 6 passed into the parent constructor.
    • Method "GetBodyLengthFromHeader(...)" should return the length of the request body according to the received head.
    • Method ResolveRequestInfo(...): "You should return an instance of your request type based on the request header and body you receive.

Actual use scenarios:

You've seen the templates of the five protocols here, and you know the format processing. Next, let's look at a network example:

Communication protocol format:

Seeing that the above protocol is in tangle with the client to send hexadecimal, how to receive the server, hexadecimal message as follows:

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

Hexadecimal or decimal, or other processes, are ultimately converted to byte []. In fact, when processing data, the data sent in the past can be converted to byte [], so the service only needs to parse the byte [] array. By parsing according to the protocol, we can get the desired data. Here's an example of using Fixed Size Receive Filter. The code is as follows:

According to the above communication protocol, start to implement parsing:

The first step is to define an appropriate data structure and protocol

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-23 21:12:30
*   2017
*   Description: Protocol Packet
*
*   Revising history:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
   public class HLData
    {
        /// <summary>
        /// Start symbol
        /// </summary>
        public char Head { get; set; }
        /// <summary>
        /// Protocol Packet Data
        /// </summary>
        public byte Ping { get; set; }
        /// <summary>
        /// Data length
        /// </summary>
        public ushort Lenght { get; set; }
        /// <summary>
        /// terminal ID
        /// </summary>
        public uint FID { get; set; }
        /// <summary>
        /// Target type
        /// </summary>
        public byte Type { get; set; }
        /// <summary>
        /// Forwarding terminal ID
        /// </summary>
        public uint SID { get; set; }
        /// <summary>
        /// Sending count
        /// </summary>
        public ushort SendCount { get; set; }
       /// <summary>
       /// Reserved fields
       /// </summary>
        public byte[] Retain { get; set; }
        /// <summary>
        /// XOR check
        /// </summary>
        public byte Check { get; set; }
        /// <summary>
        /// End symbol
        /// </summary>
        public char End { get; set; }
        
        public override string ToString()
        {
            return string.Format("Start symbol:{0},Packet data:{1},Data length:{2},terminal ID:{3},Target type:{4},Forwarding terminal ID:{5},Send Packet Count:{6},Reserved fields:{7},XOR check:{8},End symbol:{9}",
                Head, Ping, Lenght, FID, Type, SID, SendCount, Retain, Check, End);
        }


    }
}
HLData

Step 2: Establish a RequestInfo to receive server data

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SuperSocket.SocketBase.Protocol;

/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-22 21:03:31
*   2017
*   Description: Data Request
*
*   Revising history:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
   public class HLProtocolRequestInfo : RequestInfo<HLData>
    {
        public HLProtocolRequestInfo(HLData hlData)
        {
            //If you need to use the command line protocol, then the command class name HLData identical
            Initialize("HLData", hlData);
        }
    }
}
HLProtocolRequestInfo class

Step 3: FixedSize Protocol Resolution

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;

/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-22 21:06:01
*   2017
*   Description: Protocol Resolution Class, Fixed Request Size Protocol
*
*   Revising history:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
    /// <summary>
    /// Fixed Request Size Protocol,(Frame format is HLProtocolRequestInfo)
    /// </summary>
    public class HLProtocolReceiveFilter : FixedSizeReceiveFilter<HLProtocolRequestInfo>
    {
        public HLProtocolReceiveFilter() : base(25)//Total byte length 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];//Start identity parsing, 1 byte
            HLData.Ping = buffer[offset + 1];//Data, from the second place, has only one byte
            HLData.Lenght = BitConverter.ToUInt16(buffer, offset + 2);//Data length, starting at bit 3, 2 bytes
            HLData.FID = BitConverter.ToUInt32(buffer, offset + 4);//This terminal ID,Starting at bit 5, 5 bytes
            HLData.Type = buffer[offset + 9];//Target type, starting at bit 10, 1 byte
            HLData.SID = BitConverter.ToUInt32(buffer, offset + 10);//Forwarding terminal ID,Starting at the 11th bit, 5 bytes
            HLData.SendCount = BitConverter.ToUInt16(buffer, offset + 15);//Send Packet Count, Beginning at 16th Bit, 2 Bytes
            HLData.Retain = buffer.CloneRange(offset + 17, 6);//Reserve fields, starting at 18 bits, 6 bytes
            HLData.Check = buffer[offset + 23];//XOR check, starting at 24 bits, 1 byte
            HLData.End = (char)buffer[offset + 24];//End symbol, starting at bit 25, one byte
            return new HLProtocolRequestInfo(HLData);
        }
    }
}
HLProtocolReceiveFilter class

Step 4: Establishing HLReceive FilterFactory

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;

/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-23:22:01:25
*   2017
*   Description: Protocol Factory
*
*   Revising history:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
    public class HLReceiveFilterFactory: IReceiveFilterFactory<HLProtocolRequestInfo>
    {
        public IReceiveFilter<HLProtocolRequestInfo> CreateFilter(IAppServer appServer, IAppSession appSession, IPEndPoint remoteEndPoint)
        {
            return new HLBeginEndMarkReceiveFilter();
        
        }
    }
}
HLReceiveFilterFactory class

Step 5: Customize HLProtocolSession to inherit AppSession

using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Protocol;
using System;
/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-22 21:15:11
*   2017
*   Description: Custom HL Protocol Session
*
*   Revising history:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
    public class HLProtocolSession : AppSession<HLProtocolSession, HLProtocolRequestInfo>
    {
        protected override void HandleException(Exception e)
        {

        }
    
    }

}
HLProtocolSession class

Step 6: Customize HLProtocolServer to inherit AppServer

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Protocol;

/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-22 21:16:57
*   2017
*   Description: Custom server
*
*   Revising history:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
  public class HLProtocolServer : AppServer<HLProtocolSession, HLProtocolRequestInfo>
    {
        /// <summary>
        /// Using a custom protocol factory
        /// </summary>
        public HLProtocolServer()
            : base(new HLReceiveFilterFactory()) 
        {
        }
  
     
    }
}
HLProtocolServer class

Step 7, with the start-stop protocol HLBeginEndMarkReceive Filter

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SuperSocket.Common;
using SuperSocket.Facility.Protocol;

/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-23 22:07:03
*   2017
*   Description Description: A protocol with a stop sign, "&" is a start tag, "#" is an end tag, and the start and end tags are defined by themselves.
*
*   Revising history:
*
*
*****************************************************************/
namespace SuperSocketDemo
{
    public class HLBeginEndMarkReceiveFilter : BeginEndMarkReceiveFilter<HLProtocolRequestInfo>
    {
        private readonly static char strBegin = '&';
        private readonly static char strEnd = '#';
        //Start and end tags can also be two or more bytes
        private readonly static byte[] BeginMark = new byte[] { (byte)strBegin };
        private readonly static byte[] EndMark = new byte[] { (byte)strEnd };

        public HLBeginEndMarkReceiveFilter() : base(BeginMark, EndMark)
        {

        }

        /// <summary>
        /// The data parsed here will remove both the head and the tail.
        /// </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;//Define the Start Symbol by Yourself
            HLData.Ping = readBuffer[offset];//Data, from the first place, has only one byte
            HLData.Lenght = BitConverter.ToUInt16(readBuffer, offset + 1);//Data length, starting at bit 2, 2 bytes
            HLData.FID = BitConverter.ToUInt32(readBuffer, offset + 3);//This terminal ID,Starting at bit 4, 5 bytes
            HLData.Type = readBuffer[offset + 8];//Target type, starting at bit 9, 1 byte
            HLData.SID = BitConverter.ToUInt32(readBuffer, offset + 9);//Forwarding terminal ID,Starting at bit 10, 5 bytes
            HLData.SendCount = BitConverter.ToUInt16(readBuffer, offset + 14);//Send Packet Count, starting at bit 15, 2 bytes
            HLData.Retain = readBuffer.CloneRange(offset + 16, 6);//Reserve fields, starting at 17 bits, 6 bytes
            HLData.Check = readBuffer[offset + 22];//XOR check, starting from 23 bits, 1 byte
            HLData.End = strEnd;//End Symbol, Define by Yourself
            return new HLProtocolRequestInfo(HLData);
        }
    }
}
HLBeginEndMarkReceiveFilter Class

Step 8, Service Startup and Stop

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;
/****************************************************************
*   Author: Before dusk and after dawn
*   CLR Version: 4.0.30319.42000
*   Creation time: 2017-01-19 00:02:17
*   2017
*   Description: Service Start and Stop Entry  
*
*   Modify history: 2017-01-19 adjust custom mysession and myserver
*              2017 -01-23  Communication protocol parsing, direct use of entry registration events
*
*****************************************************************/
namespace SuperSocketDemo
{
    class Program
    {
        /// <summary>
        /// SuperSocket Service Start or Stop
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Console.WriteLine("Press any key to start SuperSocket service!");
            Console.ReadKey();
            Console.WriteLine();

            var HLProtocolServer = new HLProtocolServer();
           // Setting Port Number
            int port = 2017;
            //Start Application Service Port
            if (!HLProtocolServer.Setup(port)) //Start-up listening port 2017
            {
                Console.WriteLine("Service Port Startup Failure!");
                Console.ReadKey();
                return;
            }

            Console.WriteLine();
            //Register connection events
            HLProtocolServer.NewSessionConnected += HLProtocolServer_NewSessionConnected;
            //Registration request event
            HLProtocolServer.NewRequestReceived += HLProtocolServer_NewRequestReceived;
            //register Session Closing event
            HLProtocolServer.SessionClosed += HLProtocolServer_SessionClosed;
            //Attempt to start application services
            if (!HLProtocolServer.Start())
            {
                Console.WriteLine("Service Startup Failure!");
                Console.ReadKey();
                return;
            }
   
            Console.WriteLine("Server status:" + HLProtocolServer.State.ToString());
         
            Console.WriteLine("Successful service startup, please click'E'Out of Service!");

            while (Console.ReadKey().KeyChar != 'E')
            {
                Console.WriteLine();
                continue;
            }

            //Out of Service
            HLProtocolServer.Stop();
            Console.WriteLine("Services have been discontinued!");
            Console.ReadKey();
        }
      

        static void HLProtocolServer_SessionClosed(HLProtocolSession session, SuperSocket.SocketBase.CloseReason value)
        {
            Console.WriteLine(session.RemoteEndPoint.ToString() + "Connection disconnect. Cause of disconnection:" + value);
        }
        static void HLProtocolServer_NewSessionConnected(HLProtocolSession session)
        {
            Console.WriteLine(session.RemoteEndPoint.ToString() + " Connected.");
        }
        /// <summary>
        /// The protocol does not have too much complex logic, it does not need to use command mode, it can be used directly in this way.
        /// </summary>
        /// <param name="session"></param>
        /// <param name="requestInfo"></param>
        private static void HLProtocolServer_NewRequestReceived(HLProtocolSession session, HLProtocolRequestInfo requestInfo)
        {
            Console.WriteLine();
            Console.WriteLine("data sources: " + session.RemoteEndPoint.ToString());
            Console.WriteLine("Receiving data content:"+requestInfo.Body);

        }
    }
}
Class Program

Communications protocols need to be debugged using small tools. I use TCP/UDP Port Debugging Tool SocketTool V2 You can download it directly. Using HEX mode to send hexadecimal messages, the server outputs the results:

 

This article refers to official documents. Built-in common protocol implementation template

Posted by dud3r on Thu, 21 Mar 2019 18:54:52 -0700