SuperSocket⼊门(五)-常⽤协议实现模版及
FixedSizeReceiveFil。。。
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.FixedHea
derReceiveFilter, 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) //传⼊开始标记和结束标记
{
socket通信报文格式}
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>
{
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论