この記事では主に一般的なプロトコル実装テンプレートとFixedSizeReceiveFilterの例を紹介します。非常に優れた参考値です。以下のエディターで見てみましょう
ソケット通信プログラミングの最も複雑な部分は、アプリケーション層プロトコルの設計または実装が不十分な場合によく発生します。ソケット通信は粘着性のあるパッケージ、下請けは避けられません。 SuperSocket にはコマンドライン形式のプロトコル CommandLineProtocol が組み込まれています。他の形式のプロトコルを使用する場合は、カスタム プロトコル CustomProtocol を自分で実装する必要があります。ドキュメントを読むと、SuperSocket を使用してカスタム プロトコルを実装するのは簡単ではないと感じるかもしれません。 これを簡単にするために、SuperSocket にはいくつかの一般的なプロトコル解析ツールが用意されており、これらを使用して独自の通信プロトコルを簡単かつ迅速に実装できます:
TerminatorReceiveFilter (SuperSocket.SocketBase.Protocol.TerminatorReceiveFilter , SuperSocket.SocketBase) ---ターミネータープロトコル
CountSpliterReceiveFilter(SuperSocket.Facility.Protocol.CountSpliterReceiveFilter, SuperSocket.Facility) ---固定数字区切りプロトコル
FixedSizeReceiveFilter (SuperSock et.Facility tocol.FixedSizeReceiveFilter、SuperSocket.Facility) ---リクエストサイズのプロトコルを修正
BeginEndMarkReceiveFilter (SuperSocket.Facility.Protocol.BeginEndMarkReceiveFilter、SuperSocket.Facility)---開始文字と終了文字のプロトコルを使用
を修正(SuperSocket.Facility) .プロトコル.FixedHeaderReceiveFilter、SuperSocket.Facility)---ヘッダー形式は固定されており、コンテンツの長さが含まれますプロトコル
ターミネータプロトコルはコマンドラインプロトコルに似ており、一部のプロトコルはターミネータを使用して決定しますたとえば、プロトコルはターミネータとして 2 文字「##」を使用するため、クラス "TerminatorReceiveFilterFactory":
Terminator Protocol TerminatorProtocolServer:
public class TerminatorProtocolServer : AppServer { public TerminatorProtocolServer() : base(new TerminatorReceiveFilterFactory("##")) { } }
TerminatorReceiveFilter
Filter(ReceiveFilter) に基づいて受信を実装できます。 :public class YourReceiveFilter : TerminatorReceiveFilter<YourRequestInfo>
{
//More code
}
public class YourReceiveFilterFactory : IReceiveFilterFactory<YourRequestInfo> { //More code }
一部のプロトコルは次のような形式を定義しますリクエスト "#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个参数。除使用默认的过滤工厂,还可以参照上一个实例定制协议 { } }
このプロトコルでは、すべてのリクエストは同じサイズの。各リクエストが、「HUANG LI」などの 8 文字で構成される
string である場合、次のコードを使用して受信フィルター (ReceiveFilter) を実装する必要があります: class MyReceiveFilter : FixedSizeReceiveFilter<StringRequestInfo>
{
public MyReceiveFilter()
: base(8) //传入固定的请求大小
{
}
protected override StringRequestInfo ProcessMatchedRequest(byte[] buffer, int offset, int length, bool toBeCopied)
{
//TODO: 通过解析到的数据来构造请求实例,并返回
}
}
public class MyAppServer : AppServer { public MyAppServer() : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //使用默认的接受过滤器工厂 (DefaultReceiveFilterFactory) { } }
このタイプのプロトコルの各リクエストには、固定の開始マークと終了マークがあります。たとえば、すべてのメッセージが「&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: 通过解析到的数据来构造请求实例,并返回 } }
次に、この受け入れフィルター (ReceiveFilter) を AppServer クラスで使用します ):
public class MyAppServer : AppServer { public MyAppServer() : base(new DefaultReceiveFilterFactory<MyReceiveFilter, StringRequestInfo>()) //使用默认的接受过滤器工厂 (DefaultReceiveFilterFactory) { } }
このプロトコルは、リクエストを 2 つの部分に定義し、通常、最初の部分は 2 番目の部分の長さなどを定義します。最初の部分はヘッダーです。
たとえば、次のようなプロトコルがあります。ヘッダーには 6 バイトが含まれ、最初の 4 バイトはリクエストの名前を格納するために使用され、最後の 2 バイトはリクエストの長さを表すために使用されます。リクエスト本文:
/// +-------+---+-------------------------------+ /// |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 クラスに基づいて独自の受信フィルターを実装する必要があります
あなたはすでに5つのプロトコルのテンプレートを理解しており、知っています。次に、関連するフォーマットの処理を見てみましょう。
在看到上图协议是在纠结客户端发送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のサンプルコードの詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。