SuperSocket入门(五)-常用协议实现模版及FixedSizeReceiveFilter示例
2017-01-23 22:26
519 查看
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":
结束符协议
基于TerminatorReceiveFilter实现你的接收过滤器(ReceiveFilter):
实现你的接收过滤器工厂(ReceiveFilterFactory)用于创建接受过滤器实例:
2、[b]CountSpliterReceiveFilter 固定数量分隔符协议[/b]
有些协议定义了像这样格式的请求 "#part1#part2#part3#part4#part5#part6#part7#". 每个请求有7个由 '#' 分隔的部分. 这种协议的实现非常简单:
3、[b]FixedSizeReceiveFilter 固定请求大小协议[/b]
在这种协议之中, 所有请求的大小都是相同的。如果你的每个请求都是有8个字符组成的字符串,如"HUANG LI", 你应该做的事就是想如下代码这样实现一个接收过滤器(ReceiveFilter):
然后在你的 AppServer 类中使用这个接受过滤器 (ReceiveFilter):
4、[b]BeginEndMarkReceiveFilter 带起止符协议[/b]
在这类协议的每个请求之中 都有固定的开始和结束标记。例如, 我有个协议,它的所有消息都遵循这种格式 "&xxxxxxxxxxxxxx#"。因此,在这种情况下, "&" 是开始标记, "#" 是结束标记,于是你的接受过滤器可以定义成这样:
然后在你的 AppServer 类中使用这个接受过滤器 (ReceiveFilter):
5、[b]FixedHeaderReceiveFilter 头部格式固定并包含内容长度协议[/b]
这种协议将一个请求定义为两大部分, 第一部分定义了包含第二部分长度等等基础信息. 我们通常称第一部分为头部.
例如, 我们有一个这样的协议: 头部包含 6 个字节, 前 4 个字节用于存储请求的名字, 后两个字节用于代表请求体的长度:
使用 SuperSocket, 你可以非常方便的实现这种协议:
你需要基于类FixedHeaderReceiveFilter实现你自己的接收过滤器.
传入父类构造函数的 6 表示头部的长度;
方法"GetBodyLengthFromHeader(...)" 应该根据接收到的头部返回请求体的长度;
方法 ResolveRequestInfo(....)" 应该根据你接收到的请求头部和请求体返回你的请求类型的实例.
实际使用场景:
到这里五种协议的模板你都已经了解了一遍,并且知道了相关的格式处理。接下来看一个网络示例:
通讯协议格式:
Program类
通讯协议需要使用小工具进行调试,本人使用的是TCP/UDP端口调试工具SocketTool V2.大家可以直接进行下载。使用HEX模式进行发送16进制报文,服务器输出结果:
本文参考官方文档 内置的常用协议实现模版
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、[b]CountSpliterReceiveFilter 固定数量分隔符协议[/b]
有些协议定义了像这样格式的请求 "#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、[b]FixedSizeReceiveFilter 固定请求大小协议[/b]
在这种协议之中, 所有请求的大小都是相同的。如果你的每个请求都是有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、[b]BeginEndMarkReceiveFilter 带起止符协议[/b]
在这类协议的每个请求之中 都有固定的开始和结束标记。例如, 我有个协议,它的所有消息都遵循这种格式 "&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、[b]FixedHeaderReceiveFilter 头部格式固定并包含内容长度协议[/b]
这种协议将一个请求定义为两大部分, 第一部分定义了包含第二部分长度等等基础信息. 我们通常称第一部分为头部.
例如, 我们有一个这样的协议: 头部包含 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(....)" 应该根据你接收到的请求头部和请求体返回你的请求类型的实例.
实际使用场景:
到这里五种协议的模板你都已经了解了一遍,并且知道了相关的格式处理。接下来看一个网络示例:
通讯协议格式:
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示例(SuperSocket入门)
- SuperSocket入门(五)-常用协议实现模版及FixedSizeReceiveFilter示例_0
- SuperSocket与Netty之实现protobuf协议,包括服务端和客户端
- SuperSocket与Netty之实现protobuf协议,包括服务端和客户端
- SuperSocket入门(四)-命令行协议
- SuperSocket快速入门(三):实现你的AppServer和AppSession
- 使用SuperSocket实现TLV自定义协议网络通信的Demo
- 使用SuperSocket实现TLV自定义协议网络通信的Demo
- UDP: 用Socket 实现UDP 协议下的网络通信
- 基于socket简单通信协议实现
- 利用TCP和UDP协议,实现基于Socket的小聊天程序
- 在Java中实现UDP协议编程(DatagramSocket/DatagramPacket)
- 网络编程常用接口的内核实现----sys_socket()
- 在Java中实现UDP协议编程(DatagramSocket/DatagramPacket)
- Socket网络编程(二)UDP协议实现聊天工具
- 实现协议编程(DatagramSocket/DatagramPacket)
- 在Java中实现UDP协议编程(DatagramSocket/DatagramPacket)
- socket协议的java实现(附加代码)
- 移动网络应用开发中,使用 HTTP 协议比起使用 socket 实现基于 TCP 的自定义协议有哪些优势?
- Socket编程——基于TCP实现自己的通信协议