SuperSocket 2.0从入门到懵逼
- SuperSocket 2.0从入门到懵逼
附带实现, 讲解, 与部分源代码解读
1 使用SuperSocket 2.0在AspNetCore项目中搭建一个Socket服务器
1.1 引入SuperSocket 2.0
SuperSocket 2.0目前仍然处于Beta阶段, 但是功能基本可靠, 可以用于生产环境.
可以从Github源地址自行fork代码进行其它修改, 如需直接引用, 在默认的Nuget服务器是没有的, 需要添加作者自己的Nuget服务器https://www.myget.org/F/supersocket/api/v3/index.json, 获取预览版本.
1.2 在AspNetCore中搭建一个Socket服务器
使用SuperSocket 2.0快速搭建一个Socket服务器非常简单, 在框架中, 作者实现了一个SuperSocketHostBuilder进行构建, 我们要做的只是简单的配置一些参数和业务逻辑.
此外, SuperSocketHostBuilder是可以直接嵌入到AspNetCore项目的CreateHostBuilder上的, 但是并不推荐这么做…
- 配置数据包和过滤器/选择器
- 数据包, 即我们进行Socket通信时的数据包, 可以是包对象, 也可以是
byte[], 如果是包对象, 则需要在过滤器中配置解码器. - 过滤器, 作用是对数据包进行筛选然后截取一包数据, 可以将解码器挂载进来, 直接将二进制数据映射为对象.
- 选择器, 我们可以使用选择器, 来实现一个端口服务于多种协议的情况, 此时, 一个选择器则会搭配多个过滤器进行使用.
- 数据包, 即我们进行Socket通信时的数据包, 可以是包对象, 也可以是
- 配置IP和端口
- 配置IP和端口可以选择两种方式, 一种方式是从程序写入, 另一种是从配置文件中写入.
- 在本系列的介绍中, 我们大多数采用程序写入的方式, 从配置文件写入的方式, 后续会采用其它方式实现, 来扩展业务.
- 配置Session
-
在程序中作者内置了
IAppSession和AppSession供我们使用, 如果我们需要自定义Session, 则需要继承AppSession并且在程序中进行引用, 即可切换为我们定义的Session. -
自定义Session时, 由于在程序中大多数提供的参数都为
IAppSession, 所以, 需要实现SuperSocket的更多其它接口进行重写, 来维持程序运转. -
自定义Session大多数时候是为了添加更多自定义属性, 作者在设计中提供了另外的方式提供我们选择:
// AppSession源码 public class AppSession : IAppSession, ILogger, ILoggerAccessor { //...... private Dictionary<object, object> _items; public object this[object name] { get { var items = _items; if (items == null) return null; object value; if (items.TryGetValue(name, out value)) return value; return null; } set { lock (this) { var items = _items; if (items == null) items = _items = new Dictionary<object, object>(); items[name] = value; } } } //...... }可以看到, 其中内置了一个字典, 我们可以将属性的
Key-Value值直接存在字典中, 即session["prop"] = value;的形式.
-
- 配置SessionHandler
- SessionHandler会在建立和断开Socket连接时触发, 用于处理连接和断开时的业务, 可以在
SuperSocketHostBuilder中直接配置, 也可以通过重写相应的接口实现. - 连接时, 会将创建好的Session传入该方法.
- 断开时, 会将断开的Session以及触发断开的事件传入该方法.
- SessionHandler会在建立和断开Socket连接时触发, 用于处理连接和断开时的业务, 可以在
- 配置PackageHandler
- PackageHandler会在接收到数据包时触发.
- 该方法自动触发时我们将获取两个参数, 一个是Session, 一个是Package. 但是要注意的是, 这里的Session是
IAppSession类型, 并不是我们自定义的Session.- Session即为当前Socket连接, 里面附带了各种连接信息以及状态等.
- Package是通过过滤器后得到的数据包, 不过要注意的是, 例如:如果数据包为头尾标识符的数据包, 如果采用的是
byte[]的形式, 得到的可能不是原包, 而是去除了包头尾标识后的数据体. 即:7E 01 02 03 7E, 去掉头尾的7E标识得到的是01 02 03.
- Build & Run
- 构建这里同时提供了几种方式, 推荐采用
BuildAsServer(), 然后通过StartAsync()进行启用.
- 构建这里同时提供了几种方式, 推荐采用
此时, 一个Socket服务器就搭建完成了. 具体实现:
// SampleSession
public class SampleSession: AppSession
{
}
// Socket Server代码
var tcpHost = SuperSocketHostBuilder.Create<byte[], PipelineFilter>()
.ConfigureSuperSocket(options =>
{
options.AddListener(new ListenOptions
{
Ip = "Any",
Port = 4040
})
.AddListener(new ListenOptions()
{
Ip = "Any",
Port = 8888
});
})
.UseSession<JT808TcpSession>()
.UseClearIdleSession()
.UseSessionHandler(s =>
{
})
.UsePackageHandler(async (s, p) =>
{
//解包/应答/转发
})
.ConfigureErrorHandler((s, v) =>
{
})
.UseMiddleware<InProcSessionContainerMiddleware>()
.UseInProcSessionContainer()
.BuildAsServer();
await tcpHost.RunAsync();
.UseClearIdleSession()请务必调用, 在使用各类Socket框架时, 不可避免的我们的应用程序都会维持大量的僵尸连接, SuperSocket中提供了UseClearIdleSession()来自动复用已经闲置或者失去连接的资源.
2 基本的协议概念
2.1 基本协议种类
2.1.1 固定头格式协议
顾名思义, 这类协议的Header是固定的, 并且一般Header的长度是固定的, 但是也有例外情况, 此外, Header中也会包含数据体的长度等信息. 然后可以根据长度来截取一包数据.
2.1.2 固定头尾标识协议
这类协议的数据包, 前几字节和后几字节是固定的, 这样就可以通过头尾的标识来截取一包数据.
通常, 这类协议的数据包, 为了避免数据内容和协议头、尾的标识冲突, 通常会设置转义, 即将数据包中出现头尾标识的地方, 转义为其它数据, 避免识别时出现错误.
2.1.3 固定包大小协议
这类协议每一包数据的大小都是固定的, 所以可以直接根据长度进行读取.
2.1.4 命令行协议
这类协议通常以\r\n结尾, 采用字符串转为二进制流进行传输.
2.1.5 一些其它协议
PS: 关于协议的一些硬件厂商的私有协议比较奇葩, 他们的协议五花八门的…不过我们这里不做阐述, 有时间我会再讲
3 SuperSocket中的几个基本概念
3.1 Package Type
Package Type即包类型, 这里描述的是数据包的结构, 例如SuperSocket中就提供了一些基础的包类型TextPackageInfo等.
public class TextPackageInfo
{
public string Text{
get; set;}
}
这里的TextPackageInfo标识了这类类型的数据包中, 仅包含了一个字符串, 当然, 我们通常会有更复杂的网络数据包结构.例如, 我将在下列展示一个包含首尾标识的通信包, 它包含了首尾标识, 消息号, 终端Id, 以及消息体:
public class SamplePackage
{
public byte Begin{
get; set;}
public MessageId MessageId{
get; set;}
public string TerminalId{
get; set;}
public SampleBody Body{
get; set;}
public byte End{
get; set;}
}
当然, 在SuperSocket中也提供了一些接口供我们实现一些类似格式的包, 不过个人不太喜欢这种方式, 官方文档也举了一些例子, 例如,有的包会有一个特殊的字段来代表此包内容的类型. 我们将此字段命名为 “Key”. 此字段也告诉我们用何种逻辑处理此类型的包. 这是在网络应用程序中非常常见的一种设计. 例如,你的 Key 字段是整数类型,你的包类型需要实现接口IKeyedPackageInfo:
public class MyPackage : IKeyedPackageInfo<int>
{
public int Key {
get; set; }
public short Sequence {
get; set; }
public string Body {
get; set; }
}
3.2 PipelineFilter Type
这种类型在网络协议解析中作用重要. 它定义了如何将 IO 数据流解码成可以被应用程序理解的数据包. 换句话说, 就是把你的二进制流数据, 能够一包一包的识别出来, 同时可以解析成你构建的Package对象. 当然, 你也可以选择不构建, 然后将源数据直接返回.
这些是 PipelineFilter 的基本接口. 你的系统中至少需要一个实现这个接口的 PipelineFilter 类型.
public interface IPipelineFilter
{
void Reset();
obje

本文档详细介绍了如何使用SuperSocket2.0在AspNetCore环境中搭建Socket服务器,涵盖了基本的协议概念,如固定头格式、固定头尾标识和固定包大小协议。讲解了SuperSocket中的关键概念如PackageType和PipelineFilterType,并展示了如何自定义PipelineFilter以处理特定协议。此外,还讨论了扩展AppSession、实现SuperSocketService以及构建WebSocket服务器的方法。文中还提到了协议编解码器的开发预览和与其他框架如DotNetty的对比。

2489

被折叠的 条评论
为什么被折叠?



