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进制报文,效劳器输出效果:
以上就是细致引见C#经常运用协定完成模版及FixedSizeReceiveFilter的示例代码的细致内容,更多请关注ki4网别的相干文章!