SuperSocket 动态协议服务端开发全解析
SuperSocket:动态协议服务端开发全解析
在开发 TCP/Socket 服务时,我们经常会遇到 不同协议类型共存 的需求,例如一部分客户端用 分隔符协议(Terminator),另一部分客户端用 固定长度头协议(FixedHeader)。本文通过 WPF 框架结合 SuperSocket,详细讲解如何实现一个灵活可配置的服务端。
1. SuperSocket 简介
SuperSocket 是一款基于 .NET 的高性能、可扩展 TCP Socket 框架。
它的核心特点包括:
- 高性能 I/O:基于
System.IO.Pipelines
,支持异步流处理。 - 协议可扩展:通过
PipelineFilter
可以定义各种自定义协议。 - DI 与 Logging 支持:完美集成 .NET Core 的依赖注入与日志体系。
- 支持多种主机类型:可在 Console、Windows 服务、WPF 或 ASP.NET 环境中运行。
在 SuperSocket 中,核心概念有:
- TPackageInfo:代表每个完整消息包的对象,框架通过
PipelineFilter
将字节流解码成TPackageInfo
。 - PipelineFilter:字节流到消息包的解码器,支持多种协议类型(Terminator、FixedHeader、LengthField 等)。
- IPipelineFilterFactory:工厂接口,用于创建不同类型的 PipelineFilter。
- AppSession:每个客户端连接的会话对象,通过它发送或接收消息。
2. 定义消息包
在本例中,我们定义一个简单的 StringPackageInfo
:
public class StringPackageInfo
{public string Key { get; set; }public string[] Parameters { get; set; } = Array.Empty<string>();public string Body { get; set; }
}
Key
:命令名,例如ADD
。Parameters
:命令参数,例如"1 2"
。Body
:原始消息文本。
3. 自定义 PipelineFilter
3.1 Terminator 协议
public class MyTerminatorFilter : TerminatorPipelineFilter<StringPackageInfo>
{public MyTerminatorFilter(byte[] terminator) : base(terminator) { }protected override StringPackageInfo DecodePackage(ref ReadOnlySequence<byte> buffer){var text = buffer.GetString(Encoding.UTF8)?.Trim();if (string.IsNullOrEmpty(text)) return new StringPackageInfo();var parts = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);return new StringPackageInfo{Key = parts[0],Parameters = parts.Skip(1).ToArray(),Body = text};}
}
- 通过
TerminatorPipelineFilter
,我们可以指定任意分隔符(如\r\n
、$$
等)。 DecodePackage
将字节流解码成StringPackageInfo
。
3.2 FixedHeader 协议
using SuperSocket.ProtoBase;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace TcpSvrTest.Filter;public class MyFixedFilter : FixedHeaderPipelineFilter<StringPackageInfo>
{//数据格式:// -------+----------+-------------------------------------------+// 0001 | 00000010 | 4C36 3150 2D43 4D2B 4C30 3643 5055 2D43 4D2B 4C4A |// 固定头 | 数据长度 | 数据 |// 2byte | 4byte | |// -------+----------+---------------------------------+private const int HeaderSize = 6; //Header总长度public MyFixedFilter(): base(HeaderSize){}string cmdValue = "";protected override int GetBodyLengthFromHeader(ref ReadOnlySequence<byte> buffer){var reader = new SequenceReader<byte>(buffer);// ✅ 1. 检查是否有足够的头部数据if (buffer.Length < 6){// 数据不足,等待更多数据到来(返回 -1)return -1;//这会将客户端被踢掉}//先读头reader.TryReadLittleEndian(out short fix);if (!Enum.IsDefined(typeof(CommandId), (UInt16)fix)){//Console.WriteLine("不是有效的固定头");return -1;//这会将客户端被踢掉}//头转换为枚举字符串,变成Key,这样就可以路由了cmdValue = ((CommandId)fix).ToString(); ;//他真的,我哭死(太贴心了)reader.TryReadLittleEndian(out int len); //以小端方式读取short类型数据return len;}protected override StringPackageInfo DecodePackage(ref ReadOnlySequence<byte> buffer){StringPackageInfo info = new StringPackageInfo();// ✅ 3. 枚举变Keystring key = cmdValue; // 直接得到 "LOGIN", "SEND"...//跳过前面6个字节var new_buffer = buffer.Slice(HeaderSize).ToArray();//合成数据info.Key = key; // 基于StringPackageInfo的Key可以实现路由~~~~~,这样固定头也可以路由,就可以有意义了info.Body = Encoding.UTF8.GetString(new_buffer);return info;}
}
- FixedHeader 协议通过读取固定长度头部来获取消息体长度。
- 非常适合二进制协议或者自定义长度包。
4. 配置工厂实现动态协议
为了支持 不同类型客户端协议动态选择,我们实现了 ConfigurableFilterFactory
:
public class ConfigurableFilterFactory : IPipelineFilterFactory<StringPackageInfo>
{private readonly string _protocolType;private readonly string _terminator;public ConfigurableFilterFactory(string protocolType, string terminator = "\r\n"){_protocolType = protocolType;_terminator = terminator;}public IPipelineFilter<StringPackageInfo> Create(object client){return _protocolType switch{"Terminator" => new MyTerminatorFilter(Encoding.UTF8.GetBytes(_terminator)),"FixedHeader" => new MyFixedFilter(),_ => throw new NotSupportedException($"Unknown protocol: {_protocolType}")};}
}
- 通过依赖注入,我们可以在 WPF 或 Console 应用中选择协议类型。
- Terminator 的结束符也可以动态配置。
5. 构建并启动 SuperSocket 服务
在 WPF 的异步方法中:
async Task StartTcp(string protocolType, string terminator)
{host = SuperSocketHostBuilder.Create<StringPackageInfo, CommandLinePipelineFilter>() // 占位 Filter.ConfigureServices((ctx, services) =>{services.AddSingleton<ConfigurableFilterFactory>(_ => new ConfigurableFilterFactory(protocolType, terminator));services.AddSingleton<IPipelineFilterFactory<StringPackageInfo>>(sp => sp.GetRequiredService<ConfigurableFilterFactory>());}).UsePipelineFilterFactory<ConfigurableFilterFactory>() // 泛型传类型.UsePackageHandler(async (session, package) =>{// 简单示例:返回命令结果await session.SendAsync(Encoding.UTF8.GetBytes($"Received: {package.Body}\r\n"));}).ConfigureSuperSocket(options =>{options.Name = "MyTcpServer";options.Listeners = new List<ListenOptions>{new ListenOptions { Ip = "0.0.0.0", Port = 8051 }};}).ConfigureLogging((ctx, logging) =>{logging.AddConsole();logging.AddDebug();}).Build();await host.StartAsync(); // WPF UI 线程安全
}
注意点
- 不要用
RunAsync()
,因为它会阻塞线程。WPF 里用StartAsync()
更安全。 - 停止服务:
await host.StopAsync();
host = null;
6. 超级实用技巧
-
动态协议切换
- 使用
IPipelineFilterFactory
+ DI,可以在外部配置文件里控制协议类型和结束符,无需修改代码。
- 使用
-
日志和调试
- SuperSocket 内置对
Microsoft.Extensions.Logging
支持,方便在开发和生产环境打印调试信息。
- SuperSocket 内置对
-
大包支持
MaxPackageLength
可以设置为 1GB 或更多,支持大数据传输。
-
命令行或 WPF 混合使用
- SuperSocket 既可以在 Console,也可以在 WPF 窗口中运行。
- 异步模式保证 UI 不被阻塞。
7. 配置文件示例(appsettings.json)
{"Protocol": {"Type": "Terminator","Terminator": "$$"},"Server": {"Port": 8051}
}
- WPF 启动时读取配置文件即可动态选择协议和结束符。
- 支持快速切换到 FixedHeader 协议。
8. 总结
通过本文方法,你可以:
- 灵活支持多种协议类型(Terminator / FixedHeader)。
- 动态配置结束符,无需修改代码。
- 集成到 WPF 或其他 .NET 应用中。
- 利用 SuperSocket DI 与 Logging 做可扩展、可维护的高性能服务端。
SuperSocket 结合 DI + Factory 模式,是实现可扩展 TCP 服务的最佳实践。