为什么使用自己的通信协议?
通信协议用来将接收到的二进制数据转换成你的应用能够理解的请求。SuperSocket提供了一个内置的通信协议“命令行协议”,它规定每个请求必须以回车换行符“\r\n”结束。
但是一些应用可能因为各种不同的原因而无法使用“命令行协议”。针对这种情况,你需要用如下的工具来实现自己的通信协议:
RequestInfo
RequestInfo是一个代表从客户端发送来的请求的一个实体类。客户端的每一个请求都应被实例化为一个RequestInfo。RequestInfo类必须实现IRequestInfo接口,它只含有一个名为“Key”的字符串属性
public interface IRequestInfo { string Key { get; } }
我们在前面提到过,SuperSocket使用的命令行协议就用到了StringRequestInfo这个请求信息类。
你也可以根据你的应用需要来实现自己的RequestInfo类。比如,如果你所有的请求都必须包含一个DeviceID字段,你可以在RequestInfo中为其定义一个属性:
public class MyRequestInfo : IRequestInfo { public string Key { get; set; } public int DeviceId { get; set; } /* // 其他属性 */ }
SuperSocket也提供另一种请求信息类“BinaryRequestInfo”用于二进制数据的协议:
public class BinaryRequestInfo { public string Key { get; } public byte[] Body { get; } }
如果能满足你的需求,你可以直接使用BinaryRequestInfo。
ReceiveFilter
ReceiveFilter被用将二进制数据转换为请求信息实例。
你需要实现IReceiveFilter接口来实现ReceiveFilter类。:
public interface IReceiveFilter<TRequestInfo> where TRequestInfo : IRequestInfo { /// <summary> /// Filters received data of the specific session into request info. /// </summary> /// <param name="readBuffer">The read buffer.</param> /// <param name="offset">The offset of the current received data in this read buffer.</param> /// <param name="length">The length of the current received data.</param> /// <param name="toBeCopied">if set to <c>true</c> [to be copied].</param> /// <param name="rest">The rest, the length of the data which hasn't been parsed.</param> /// <returns></returns> TRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest); /// <summary> /// Gets the size of the left buffer. /// </summary> /// <value> /// The size of the left buffer. /// </value> int LeftBufferSize { get; } /// <summary> /// Gets the next receive filter. /// </summary> IReceiveFilter<TRequestInfo> NextReceiveFilter { get; } /// <summary> /// Resets this instance to initial state. /// </summary> void Reset(); }
- TRequestInfo:类型参数“TRequestInfo”是你在应用中使用的请求信息类。
- LeftBufferSize:在请求过滤器中缓存的数据大小。
- NextReceiveFilter:当下一段二进制数据被接收到时,奖杯使用的请求过滤器。
- Reset():将该实例重置为初始状态。
- Filter(...):Filter方法在二进制数据被SuperSocket接收到时执行,接收到的数据分配在参数readBuffer中。因为在一个AppServer实例中,所有连接共享readBuffer,所以你需要从“offset”(方法参数)位置,并使用“length”(方法参数)来读取接收到的数据。
TRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest);
- readBuffer:接收缓冲区,接收到的数据被存放在里面。
- offset:接收到的数据在readBuffer中的开始位置。
- length:接收数据的长度。
- toBeCopied:当我们想要缓存数据在readBuffer中时,表明是否一根创建一份readBuffer的拷贝而不是直接使用。
- rest:这是一个输出参数,它应该在你找到完整的请求后被设置为剩余接收数据的大小。
- 你还需要处理各种情况:
- 如果你从接收到的数据中找到了一个完整的请求,你必须返回一个你的请求信息类型的请求信息实例。
- 如果你没有找到完整的请求,那么就返回NULL。
- 如果你找到了一个完整的请求,但接收到的数据不止一个请求,那么将输出参数“left”设置为剩余数据大小。如果它大于0,SuperSocket将检查输出参数“rest”,Filter方法将以修正后的“offset”和“length”参数被再次执行。
ReceiveFilterFactory
ReceiveFilterFactory用于为每个会话创建接收过滤器。要定义你的ReceiveFilterFactory类,必须实现IReceiveFilterFactory接口。类型参数“TRequestInfo”是你希望运行在应用中的请求信息类。
/// <summary> /// Receive filter factory interface /// </summary> /// <typeparam name="TRequestInfo">The type of the request info.</typeparam> public interface IReceiveFilterFactory<TRequestInfo> : IReceiveFilterFactory where TRequestInfo : IRequestInfo { /// <summary> /// Creates the receive filter. /// </summary> /// <param name="appServer">The app server.</param> /// <param name="appSession">The app session.</param> /// <param name="remoteEndPoint">The remote end point.</param> /// <returns> /// the new created request filer assosiated with this socketSession /// </returns> IReceiveFilter<TRequestInfo> CreateFilter(IAppServer appServer, IAppSession appSession, IPEndPoint remoteEndPoint); }
你也可以使用默认的接收过滤器工厂。
DefaultReceiveFilterFactory
与AppSession和AppServer协同工作
现在你有了RequestInfo,ReceiveFilter和ReceiveFilterFactory,但你还不能开始使用它们。如果你想使得它们在应用中可用,你需要定义使用了你创建的RequestInfo,ReceiveFilter和ReceiveFilterFactory的AppSession和AppServer。
- 为AppSession设置RequestInfo
public class YourSession : AppSession<YourSession, YourRequestInfo> { //更多代码 }
public class YourAppServer : AppServer<YourSession, YourRequestInfo> { public YourAppServer() : base(new YourReceiveFilterFactory()) { } }
完成上面两件事后,你的自定义通信协议就可以工作了。
没有评论:
发表评论