首页 > 技术文章 > SuperSocket 2.0应用1:基于固定头协议的Socket服务器

xhubobo 原文

SuperSocket:GitHubSuperSocket 2.0 中文文档

本文开发环境:Win10 + VS2019 + .NetCore 3.1 + SuperSocket 2.0.0-beta.8。

Gitee:SuperSocketV2FixedHeaderSample

本文使用SuperSocket 2.0创建基于固定头协议的Socket服务器,能够满足大部分自定义协议的服务器需求。本文使用协议如下所示,围绕该协议说明PipelineFilter、PackageInfo、IAsyncCommand、AppSession、SuperSocketService等SuperSocket2.0元素是如何构建一个完整服务器的。

本文尽可能的将实现Socket服务器的类的作用说清楚,基本上就没什么代码了,具体可以参考Gitee相关项目。

0、准备工作

使用VS2019创建一个基于.NET Core的控制台程序,这里目标框架选择了.NET Core 3.1。

使用NuGet工具搜索SuperSocket(需要勾选“包括预发行版”),选择最新的版本(目前是2.0.0-beta.8)安装。

在项目根目录添加appsettings.json配置文件,并设置其文件属性为“如果较新则复制”。(参考SuperSocket 2.0学习02:通过配置启动 SuperSocket)。

1、PipelineFilter

SuperSocket 2.0使用PipelineFilter来处理应用层通信协议,其内置的FixedHeaderPipelineFilter实现了头部格式固定并且包含内容长度的协议,从这个类继承就能实现解析自己业务相关的固定头协议处理类。

FixedHeaderPipelineFilter的构造函数需要传入包头大小,其余的包内容即为数据内容。根据我们的协议,包头大小为4,因此将4传入base类的构造函数即可。

其次是获取数据长度,通过重载GetBodyLengthFromHeader实现。我们只需要在传入的buffer中跳过2个字节,(以大端方式)读取一个short类型数值即可,因为长度是2个字节。被我们跳过的字节很有用,会在解包时候保留该字节。

然后是通过重载DecodePackage解包,前面2个字节是类型,然后2个字节是数据长度,剩下的字节就是包体内容了。2个字节的类型,用于区分是什么命令,因此这里的类型和具体的命令就联系起来了。

2、PackageInfo

前面解析了固定头协议的数据包,就得有数据结构存放包数据,这就用到了PackageInfo。

PackageInfo需要作为PipelineFilter的泛型类型参数,以便在解包的时候能够获取对应包的对象。根据我们定义的协议,代表包类型的前2个字节是需要保存的,然后是包体字节。至于2个长度字节,则在进行包校验的时候是必须保留的,否则就可以在PackageInfo类型中将其舍弃。

不得不说,仅仅如此是不能够实现固定头协议的命令映射的。SuperSocket2.0中的命令接口IAsyncCommand如果添加了Command标记,其泛型参数PackageInfo类型就必须实现IKeyedPackageInfo接口,以便将Command标记中的Key参数和IKeyedPackageInfo实例中的Key字段做映射。

我们的协议中Type是2个字节,因此将short类型作为IKeyedPackageInfo的泛型参数就再合适不过了,只是在解包的时候需要处理好大端小端的问题,以便获取正确的short类型Key值。

3、IAsyncCommand

SuperSocket2.0中的命令类MyCommand需要实现IAsyncCommand接口(我们默认都是异步命令),在其ExecuteAsync方法中处理前面实现的PackageInfo对象。

要将MyCommand类和具体的Type进行绑定,需要在MyCommand类中添加Command属性,标记其Key值为对应的协议即可。根据前面描述,其类型为short。如果Type为0xFF, 0xFE,那么MyCommand标记如下:

/// <summary>
/// 0xFF 0xFE
/// </summary>
[Command(Key = unchecked((short)(0xFF * 256 + 0xFE)))]
public class MyCommand : IAsyncCommand<MyPackageInfo>

这样,MyCommand类就能将对应协议的Type类型对应起来,并在ExecuteAsync方法中对其进行业务处理。

4、AppSession

根据官方描述,AppSession代表一个和客户端的逻辑连接,基于连接的操作应该定于在该类之中。你可以用该类的实例发送数据到客户端,接收客户端发送的数据或者关闭连接。

对于实现固定头协议来说,AppSession不是必须的,但是站在应用服务器的角度来说,一个AppSession对象就是一个客户端连接,该客户端起码会含有标识自己的信息,这是应用服务器所需要的。

AppSession是面向应用业务的类。

5、SuperSocketService

官方所述,SuperSocketService代表了监听所有客户端连接的服务器实例,宿主所有连接。应用程序级别的操作和业务逻辑可以定义在里面。

说白了,SuperSocketService就是实实在在的一个服务器对象,是应用级别的。在这个类中可以向用户连接批量发送数据,比如进行一些事件监听的客户,在数据更新时候通过SuperSocketService可以跟注册监听的用户同步。

但是SuperSocket2.0又支持多服务器实例,多服务器实例在.NET Core中是使用一个进程承载的,每个实例以HostedService的形式在进程(Host)中运行。如果要实现多进程或多服务器运行,需要使用Docker等容器技术。

6、Main函数

控制台程序的入口就是这里了,在这个函数中可以进行SuperSocket宿主的创建,命令、AppSession以及SuperSocketService的注入,配置日志等。总之,和.NET Core服务相关的配置都在这里进行。最后,宿主执行RunAsync方法就将Socket服务器启动起来了。

推荐阅读