WSHub.FreeRedis
0.0.2
dotnet add package WSHub.FreeRedis --version 0.0.2
NuGet\Install-Package WSHub.FreeRedis -Version 0.0.2
<PackageReference Include="WSHub.FreeRedis" Version="0.0.2" />
<PackageVersion Include="WSHub.FreeRedis" Version="0.0.2" />
<PackageReference Include="WSHub.FreeRedis" />
paket add WSHub.FreeRedis --version 0.0.2
#r "nuget: WSHub.FreeRedis, 0.0.2"
#:package WSHub.FreeRedis@0.0.2
#addin nuget:?package=WSHub.FreeRedis&version=0.0.2
#tool nuget:?package=WSHub.FreeRedis&version=0.0.2
WSHub
基于 .NET 的现代 WebSocket 即时通讯服务框架,参考并改进自 FreeIM。
职责清晰、可扩展、支持多节点部署,适用于游戏、IM、实时协作等场景。
特性
- 发送与投递分离 —
IMessageHandler(发送) +IMessageDeliveryHandler(投递)独立管道 - 多节点支持 — 水平扩展,通过 Redis(FreeRedis)或 InMemory 进行消息路由
- 后端语义一致 — InMemory 与 FreeRedis 共用同一套 ChannelBus 键、引用计数订阅/退订与本地投递语义,并由
RoomDemoFreeRedisE2ETests在真实 Redis 上交叉验收 - 多租户隔离 — 租户段由 Hub 强制注入总线键(
{prefix}:ch:{tenantId}:{channelId}),业务层无法绕过 - 二进制消息 — byte[] 管道 + 路由头(0xFE),支持 protobuf
- 连接收口 —
ConnectionCloser是断连与空闲回收的唯一路径:停发送队列 → 退频道 → 注销 presence → 关 socket - 活跃度保活 + 回板兜底 — 接收循环/心跳刷新 connIndex TTL;进程崩溃由宿主周期任务清理
- 事件机制 — 上线/下线/断连/频道进出事件
- 多目标框架 —
net8.0(LTS) /net9.0/net10.0 - Native AOT — InMemory 模式支持 Docker AOT 部署
- 测试规模 — 662 个测试(单元 + Testcontainers/Redis 集成 + AOT 容器,详见 CI)
快速开始
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddWSHub<long>(options =>
{
options.KeyPrefix = "wshub_v1";
options.Servers = new[] { "localhost:5299" };
options.Server = "localhost:5299";
})
.AddClientIdConverter<long, LongClientIdConverter>()
.UseInMemoryStorage<long>() // 开发环境;生产用 .UseFreeRedisStorage(redis)
.AddDefaultHandlers<long>(); // 一键注册 Handler + Dispatcher + 后台服务
var app = builder.Build();
app.UseWebSockets();
app.Map("/ws/{tenantId?}", async (string? tenantId, HttpContext context) =>
{
var handler = context.RequestServices.GetRequiredService<WebSocketConnectionHandler<long>>();
await handler.HandleAsync(tenantId, context);
});
app.Run();
完整示例见 samples/MinimalApiSample/。
文档
| 文档 | 用途 |
|---|---|
docs/ARCHITECTURE-MAP.md |
入口。架构对照稿 + 代码导航:设想 vs 现在的差异、冻结边界、键表、核对某条结论该看哪个文件 |
docs/ARCHITECTURE.md |
规范性架构定义:边界、约束(C1–C6)、发布目标、Redis 键布局 |
docs/PROTOCOL.md |
调用方契约:Control API、WebSocket 帧、202 语义、反模式 |
docs/DEMO-GAPS.md |
做 RoomDemo 时实际撞到的 Hub 能力缺口(含 path 证据),下一步选型的输入 |
架构概览
客户端 WebSocket ──→ ProcessClientMessage ──→ MessageHandlerDispatcher ──→ IMessageHandler (发送)
│
应用层主动发送 ──→ IApplicationMessageService ──→ MessageHandlerDispatcher ──→ IMessageHandler (发送)
│
IMessagePublisher
│
Redis / InMemory 消息总线
│
IMessageListener
│
HandleIncomingMessage
│
MessageDeliveryDispatcher ──→ IMessageDeliveryHandler (投递)
│
ConnectionManager ──→ WebSocket
项目结构
WSHub/
├── src/
│ ├── WSHub.Abstractions/ # 接口、模型、Options
│ ├── WSHub.AspNetCore/ # ASP.NET Core 集成
│ ├── WSHub.Host/ # Control HTTP API 独立宿主
│ ├── WSHub.InMemory/ # InMemory 存储后端(开发/测试)
│ └── WSHub.FreeRedis/ # FreeRedis 存储后端(生产/多节点)
├── samples/
│ ├── MinimalApiSample/ # 基础示例 + 测试页面
│ ├── ImDemo/ # ⚠ 绕开 Hub 的连接循环,仅作玩法参考
│ ├── GameDemo/ # Protobuf HTML5 实时游戏
│ ├── RoomDemo/ # 房间基础能力对照 + 可切 FreeRedis 后端的交叉验收
│ ├── RoomChatDemo/ # 多房间同时订阅 + 样例侧在线列表
│ ├── OfficialImDemo/ # 私聊/群聊走正式连接路径;历史显式标注为样例能力
│ └── AotDemo/ # Native AOT Docker 部署示例
├── benchmarks/
│ └── WSHub.Benchmarks/ # BenchmarkDotNet 性能基准
├── tests/ # 7 个测试项目,662 测试
├── docs/ # 架构对照稿 + 架构规范 + 协议契约 + 性能分析
└── .github/workflows/ # CI/CD (coverage + publish + DingTalk)
性能 (BenchmarkDotNet)
| 操作 | 延迟 | 说明 |
|---|---|---|
PublishAsync |
140 ns | 单条消息发布 |
PublishBatchAsync ×100 |
13.9 μs | 批量发布 |
RegisterAsync |
1.08 μs / 248 B | 连接注册 |
HasOnlineAsync |
2.3 ns | 在线检查 |
Samples
| 项目 | 说明 |
|---|---|
| MinimalApiSample | 基础 WebSocket + Channel + Broadcast |
| GameDemo | Protobuf HTML5 多人实时游戏(手写 JS protobuf 编解码器) |
| RoomDemo | 房间基础能力对照:走正式连接路径,重连补偿显式标注为样例行为;Storage:Backend=FreeRedis 可切生产后端做交叉验收 |
| RoomChatDemo | 一条连接同时订阅多个房间;样例侧「在线成员」是 GetOnlineClientIdsAsync 的近似,偏差由测试显式断言 |
| OfficialImDemo | 私聊 dm:{min}:{max} / 群聊走正式连接路径;好友与群成员校验在样例(403);内存历史标注为样例能力 |
| ImDemo | ⚠ 绕开 Hub(自建连接循环),只作业务玩法参考,不能用于验证 Hub 能力 |
| AotDemo | Native AOT + Docker 部署(docker compose up -d) |
运行样例
dotnet run --project samples/RoomDemo # http://localhost:5300
dotnet run --project samples/RoomChatDemo # http://localhost:5500
dotnet run --project samples/OfficialImDemo # http://localhost:5400
dotnet run --project samples/ImDemo # http://localhost:5000 ⚠ 绕开 Hub,仅作玩法参考
多目标框架的样例(net8.0;net9.0;net10.0)必须显式指定框架,否则 dotnet run 报
Your project targets multiple frameworks:
dotnet run --project samples/GameDemo --framework net10.0 # http://localhost:5200
dotnet run --project samples/MinimalApiSample --framework net10.0 # http://localhost:5299
端口来自各自的 Properties/launchSettings.json,必须与 Program.cs 里的
options.Server / options.Servers 一致 —— 连接端点返回的 ws:// 地址就是按它拼的。
每个样例页面顶部都写了操作顺序(先连接,再进房/进会话,才收得到消息)。 只连上但没进房时,发送仍然返回 200,但 Hub 无从投递给你自己。
运行测试
dotnet test tests/WSHub.Abstractions.Tests/
dotnet test tests/WSHub.AspNetCore.Tests/
dotnet test tests/WSHub.InMemory.Tests/
dotnet test tests/WSHub.FreeRedis.Tests/
dotnet test tests/WSHub.IntegrationTests/
dotnet test tests/WSHub.Samples.Tests/ # 样例端到端(含 RoomDemo 的 FreeRedis 交叉验收)
dotnet test tests/WSHub.AotTests/ # 需要 Docker:构建 AOT 镜像并起容器
WSHub.Samples.Tests 里的 RoomDemoFreeRedisE2ETests 会起一个 redis:7-alpine Testcontainer,
用真实 Redis 把 RoomDemo 的「成员收到、非成员收不到」重跑一遍 —— 样例代码不变,只切
Storage:Backend。测试机上已有 Redis 时可设 REDIS_CONNECTION_STRING 复用,跳过启动容器。
运行 Benchmark
dotnet run -c Release --project benchmarks/WSHub.Benchmarks/
CI/CD
| Workflow | 触发 | 说明 |
|---|---|---|
coverage.yml |
push / PR | 覆盖率报告 (dotnet-coverage) |
publish-nuget.yml |
release / manual | NuGet 发布 + 钉钉通知 |
贡献
欢迎提交 Issue 和 Pull Request。
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FreeRedis (>= 1.3.7)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- WSHub.Abstractions (>= 0.0.2)
-
net8.0
- FreeRedis (>= 1.3.7)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- System.Text.Json (>= 9.0.0)
- WSHub.Abstractions (>= 0.0.2)
-
net9.0
- FreeRedis (>= 1.3.7)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- WSHub.Abstractions (>= 0.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.2 | 77 | 10/1/2026 |
| 0.0.1 | 73 | 10/1/2026 |
| 0.0.0.41 | 148 | 8/11/2026 |
| 0.0.0.40 | 104 | 8/11/2026 |
| 0.0.0.39 | 100 | 8/8/2026 |
| 0.0.0.38 | 118 | 8/8/2026 |
| 0.0.0.37 | 105 | 8/8/2026 |
| 0.0.0.36 | 104 | 8/8/2026 |
| 0.0.0.35 | 109 | 8/8/2026 |
| 0.0.0.34 | 109 | 8/7/2026 |
| 0.0.0.33 | 144 | 8/5/2026 |
| 0.0.0.32 | 159 | 7/27/2026 |
| 0.0.0.31 | 100 | 7/27/2026 |
| 0.0.0.30 | 109 | 7/27/2026 |
| 0.0.0.29 | 107 | 7/26/2026 |
| 0.0.0.27 | 111 | 7/26/2026 |
| 0.0.0.26 | 117 | 7/26/2026 |
| 0.0.0.25 | 205 | 7/2/2026 |
| 0.0.0.24 | 108 | 7/1/2026 |
| 0.0.0.22 | 130 | 7/1/2026 |