Skip to content

Cyaim/WebSocketServer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

305 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WebSocketServer

996ICU Version NuGet Build Code Size License
996.icu NuGet Code size LicenseLICENSE

WebSocketServer is a lightweight and high-performance WebSocket library. Supports routing, full-duplex communication, clustering, and multi-language client SDKs.

📚 Documentation Center / 文档中心

✨ Features / 特性

  • Lightweight & High Performance - Based on ASP.NET Core
  • Routing System - MVC-like routing mechanism
  • Full Duplex Communication - Bidirectional communication support
  • Multi-node Cluster - Raft-based consensus protocol
  • Multi-language Clients - C#, TypeScript, Rust, Java, Dart, Python
  • Automatic Endpoint Discovery - Client SDKs auto-discover server endpoints
  • Streaming Upload - [WebSocket(Stream = true)] streams large files with constant server memory / 大文件流式上传,服务端内存恒定
  • Receive Memory Control - Per-connection / per-endpoint / global receive caps, safe by default / 三级接收内存封顶,默认即安全
  • Dashboard - Real-time monitoring and statistics

QuickStart

1. Install Library / 安装库

# Package Manager
Install-Package Cyaim.WebSocketServer

# .NET CLI
dotnet add package Cyaim.WebSocketServer

# PackageReference
<PackageReference Include="Cyaim.WebSocketServer" Version="2.0.0" />

2. Configure WebSocket Server / 配置 WebSocket 服务器

Using Minimal API / 使用 Minimal API

Two lines are all you need — AddWebSocketServer() automatically wires an MvcChannelHandler on channel /ws and discovers all [WebSocket] endpoints in your *.Controllers namespace.
只需两行代码 —— AddWebSocketServer() 会自动在 /ws 通道上挂载 MvcChannelHandler,并自动发现 *.Controllers 命名空间下所有标记了 [WebSocket] 的终结点。

using Cyaim.WebSocketServer.Infrastructure;
using Cyaim.WebSocketServer.Middlewares;

var builder = WebApplication.CreateBuilder(args);

// Add WebSocket server (default channel: /ws) / 添加 WebSocket 服务器(默认通道:/ws)
builder.Services.AddWebSocketServer();

var app = builder.Build();

app.UseWebSockets();
app.UseWebSocketServer();

app.Run();

Need more channels or options? Use the fluent builder or the configure overload:
需要更多通道或配置?使用流式构建器或配置重载:

// Custom channels / 自定义通道
builder.Services.AddWebSocketServer()
    .AddMvcChannel("/im")                          // MVC-style channel / MVC 风格通道
    .AddChannel("/chat", myHandler.ConnectionEntry); // Custom handler / 自定义处理器
// Note: explicitly adding channels replaces the auto default "/ws".
// 注意:显式添加通道会替换自动默认的 "/ws" 通道。

// Custom options / 自定义配置
builder.Services.AddWebSocketServer(x =>
{
    x.MaxConnectionLimit = 10000;
    x.WatchAssemblyNamespacePrefix = "MyApp.WsControllers"; // Endpoint scan prefix / 终结点扫描前缀
});

Using Startup.cs / 使用 Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddWebSocketServer();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseWebSockets();
    app.UseWebSocketServer();
}

Advanced: Manual Configuration / 高级:手动配置

ConfigureWebSocketRoute remains fully supported when you want full manual control (channel table, service collection, etc.):
当您需要完全手动控制(通道表、服务容器等)时,仍可使用 ConfigureWebSocketRoute

using Cyaim.WebSocketServer.Infrastructure.Handlers.MvcHandler;
using Cyaim.WebSocketServer.Infrastructure.Configures;
using Cyaim.WebSocketServer.Middlewares;

var builder = WebApplication.CreateBuilder(args);

// Configure WebSocket route manually / 手动配置 WebSocket 路由
builder.Services.ConfigureWebSocketRoute(x =>
{
    var mvcHandler = new MvcChannelHandler();
    x.WebSocketChannels = new Dictionary<string, WebSocketRouteOption.WebSocketChannelHandler>()
    {
        { "/ws", mvcHandler.ConnectionEntry }
    };
    x.ApplicationServiceCollection = builder.Services;
});

var app = builder.Build();

// Configure WebSocket options / 配置 WebSocket 选项
var webSocketOptions = new WebSocketOptions()
{
    KeepAliveInterval = TimeSpan.FromSeconds(120),
};
app.UseWebSockets(webSocketOptions);
app.UseWebSocketServer();

app.Run();

3. Mark WebSocket Endpoints / 标记 WebSocket 端点

Add [WebSocket] attribute to your controller actions:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [WebSocket]  // Mark as WebSocket endpoint / 标记为 WebSocket 端点
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        }).ToArray();
    }
}

Note: The target parameter in requests is case-insensitive.
注意: 请求中的 target 参数不区分大小写。

Request and Response

Scheme namespace 👇
Request Cyaim.WebSocketServer.Infrastructure.Handlers.MvcRequestScheme
Response Cyaim.WebSocketServer.Infrastructure.Handlers.MvcResponseScheme

Request target ignore case

Request scheme

1. Nonparametric method request

{
	"target": "WeatherForecast.Get",
	"body": {}
}

This request will be located at "WeatherForecastController" -> "Get" Method.

Response to this request

{
	"Target": "WeatherForecast.Get"
	"Status": 0,
	"Msg": null,
	"RequestTime": 637395762382112345,
	"CompleteTime": 637395762382134526,
	"Body": [{
		"Date": "2020-10-30T13:50:38.2133285+08:00",
		"TemperatureC": 43,
		"TemperatureF": 109,
		"Summary": "Scorching"
	}, {
		"Date": "2020-10-31T13:50:38.213337+08:00",
		"TemperatureC": 1,
		"TemperatureF": 33,
		"Summary": "Chilly"
	}]
}

Forward invoke method return content will write MvcResponseScheme.Body.

2. Request with parameters

Example Code:

  1. Change method code to:
[WebSocket]
[HttpGet]
public IEnumerable<WeatherForecast> Get(Test a)
{
    var rng = new Random();
    return Enumerable.Range(1, 2).Select(index => new WeatherForecast
    {
         TemperatureC = a.PreTemperatureC + rng.Next(-20, 55),
         Summary = a.PreSummary + Summaries[rng.Next(Summaries.Length)]
    }).ToArray();
}
  1. Define parameter class
public class Test
{
    public string PreSummary { get; set; }
    public int PreTemperatureC { get; set; }
}

Request parameter

{
	"target": "WeatherForecast.Get",
	"body": {
	    "PreSummary":"Cyaim_",
	    "PreTemperatureC":233
	}
}

Request body will be deserialized and passed to the method parameter.

Response to this request

{
	"Target": "WeatherForecast.Get",
	"Status": 0,
	"Msg": null,
	"RequestTime": 0,
	"CompleteTime": 637395922139434966,
	"Body": [{
		"Date": "0001-01-01T00:00:00",
		"TemperatureC": 282,
		"TemperatureF": 539,
		"Summary": "Cyaim_Warm"
	}, {
		"Date": "0001-01-01T00:00:00",
		"TemperatureC": 285,
		"TemperatureF": 544,
		"Summary": "Cyaim_Sweltering"
	}]
}

Client SDKs / 客户端 SDK

We provide multi-language client SDKs with automatic endpoint discovery:

Quick Example / 快速示例

C# Client:

using Cyaim.WebSocketServer.Client;

var factory = new WebSocketClientFactory("http://localhost:5000", "/ws");
var client = await factory.CreateClientAsync<IWeatherService>();
var forecasts = await client.GetForecastsAsync();

TypeScript Client:

import { WebSocketClientFactory } from '@cyaim/websocket-client';

const factory = new WebSocketClientFactory('http://localhost:5000', '/ws');
const client = await factory.createClient<IWeatherService>({
  getForecasts: async () => {}
});
const forecasts = await client.getForecasts();

For more details, see: Clients Documentation | 客户端文档

Streaming Upload & Memory Control / 流式上传与接收内存控制

Ordinary endpoints buffer each message (default cap 4 MiB, configurable via MaxRequestReceiveDataLimit or per-endpoint [WebSocket(MaxBytes = N)]). To transfer large files, mark the endpoint Stream = true — the payload is fed to your endpoint as a Stream without buffering, so server memory stays constant.
普通端点会整条缓冲消息(默认上限 4 MiB,可用 MaxRequestReceiveDataLimit 或端点级 [WebSocket(MaxBytes = N)] 调整)。要传大文件,把端点标成 Stream = true,负载会作为 Stream 边收边喂给端点,服务端内存恒定。

// Server: a streaming upload endpoint / 服务端:流式上传端点
[WebSocket("file.upload", Stream = true, MaxBytes = 2L * 1024 * 1024 * 1024)]
public async Task<object> Upload(UploadMeta meta, Stream body, CancellationToken ct)
{
    await using var fs = File.Create(Path.Combine(dir, meta.FileName));
    await body.CopyToAsync(fs, ct);          // constant memory, end-to-end backpressure
    return new { bytes = fs.Length };
}
// Client: upload with the uploadStream helper (all 6 SDKs provide one) / 客户端:uploadStream 帮助方法
const res = await client.uploadStream("file.upload", fs.createReadStream("big.bin"), { fileName: "big.bin" });

⚠️ 2.0 behaviour change: MaxRequestReceiveDataLimit now defaults to 4 MiB (was unlimited). If you send single messages larger than 4 MiB through ordinary endpoints, raise it (or set null), or use a streaming endpoint. / 2.0 行为变更:该上限默认从"不限"改为 4 MiB,大消息需显式调大或改用流式端点。

For more details, see: Streaming Upload & Memory Control | 流式上传与内存控制

Cluster / 集群

Cyaim.WebSocketServer supports multi-node clustering with Raft consensus protocol. You can use WebSocket, Redis, or RabbitMQ for inter-node communication.

Basic Cluster Setup / 基础集群配置

using Cyaim.WebSocketServer.Infrastructure.Cluster;
using Cyaim.WebSocketServer.Infrastructure.Configures;

var builder = WebApplication.CreateBuilder(args);

// Configure WebSocket route / 配置 WebSocket 路由
builder.Services.ConfigureWebSocketRoute(x =>
{
    var mvcHandler = new MvcChannelHandler();
    x.WebSocketChannels = new Dictionary<string, WebSocketRouteOption.WebSocketChannelHandler>()
    {
        { "/ws", mvcHandler.ConnectionEntry }
    };
    x.ApplicationServiceCollection = builder.Services;
});

var app = builder.Build();

// Configure WebSocket / 配置 WebSocket
app.UseWebSockets();
app.UseWebSocketServer(serviceProvider =>
{
    // Configure cluster / 配置集群
    var clusterOption = new ClusterOption
    {
        NodeId = "node1",
        NodeAddress = "localhost",
        NodePort = 5000,
        TransportType = "ws", // or "redis" or "rabbitmq"
        ChannelName = "/cluster",
        Nodes = new[]
        {
            "ws://localhost:5001/node2",
            "ws://localhost:5002/node3"
        }
    };
    
    return clusterOption;
});

app.Run();

Using Redis Transport / 使用 Redis 传输

# Install Redis transport package / 安装 Redis 传输包
dotnet add package Cyaim.WebSocketServer.Cluster.StackExchangeRedis
var clusterOption = new ClusterOption
{
    NodeId = "node1",
    TransportType = "redis",
    RedisConnectionString = "localhost:6379",
    ChannelName = "/cluster",
    Nodes = new[] { "node1", "node2", "node3" }
};

Using RabbitMQ Transport / 使用 RabbitMQ 传输

# Install RabbitMQ transport package / 安装 RabbitMQ 传输包
dotnet add package Cyaim.WebSocketServer.Cluster.RabbitMQ
var clusterOption = new ClusterOption
{
    NodeId = "node1",
    TransportType = "rabbitmq",
    RabbitMQConnectionString = "amqp://guest:guest@localhost:5672/",
    ChannelName = "/cluster",
    Nodes = new[] { "node1", "node2", "node3" }
};

For more details, see: Cluster Documentation | 集群文档

📖 More Documentation / 更多文档

🔗 Related Links / 相关链接

📄 License / 许可证

This project is licensed under MIT License.

Copyright © Cyaim Studio

About

WebSocketServer is lightweight and high performance WebSocket library.support route, full duplex communication.

Resources

License

Stars

9 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors