This document provides detailed information about the features and usage of the Cyaim.WebSocketServer core library.
- Overview
- Quick Start
- Routing System
- Handlers
- Middleware
- Configuration Options
- Request and Response
- Advanced Features
Cyaim.WebSocketServer is a lightweight, high-performance WebSocket server library that provides MVC-like routing mechanisms, supporting full-duplex communication, multiplexing, and pipeline processing.
- ✅ Lightweight & High Performance - Based on ASP.NET Core, excellent performance
- ✅ Routing System - MVC-like routing mechanism, supports RESTful API
- ✅ Full Duplex Communication - Supports bidirectional communication
- ✅ Multiplexing - Single connection supports multiple requests/responses
- ✅ Pipeline Processing - Supports middleware pipeline pattern
- ✅ Type Safety - Strongly typed parameter binding and responses
dotnet add package Cyaim.WebSocketServerusing Cyaim.WebSocketServer.Infrastructure.Handlers.MvcHandler;
using Cyaim.WebSocketServer.Infrastructure.Configures;
var builder = WebApplication.CreateBuilder(args);
// Configure WebSocket route
builder.Services.ConfigureWebSocketRoute(x =>
{
// Define channels
x.WebSocketChannels = new Dictionary<string, WebSocketRouteOption.WebSocketChannelHandler>()
{
{ "/ws", new MvcChannelHandler(4 * 1024).ConnectionEntry }
};
x.ApplicationServiceCollection = builder.Services;
});var app = builder.Build();
// Configure WebSocket options
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120)
};
app.UseWebSockets(webSocketOptions);
app.UseWebSocketServer();using Cyaim.WebSocketServer.Infrastructure.Attributes;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[WebSocket]
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return new[]
{
new WeatherForecast { Date = DateTime.Now, TemperatureC = 25 }
};
}
}Channels are entry points for WebSocket connections, with each channel corresponding to a handler.
x.WebSocketChannels = new Dictionary<string, WebSocketRouteOption.WebSocketChannelHandler>()
{
{ "/ws", mvcHandler.ConnectionEntry }, // MVC handler
{ "/api", customHandler.ConnectionEntry } // Custom handler
};Use the [WebSocket] attribute to mark WebSocket endpoints:
[WebSocket]
[HttpGet]
public string GetMessage()
{
return "Hello, WebSocket!";
}Note: The method parameter of the [WebSocket] attribute is case-insensitive.
MvcChannelHandler is the default MVC-style handler that supports routing to controllers and action methods.
// Receive and send buffer sizes (default 4KB)
var handler = new MvcChannelHandler(
receiveBufferSize: 4 * 1024,
sendBufferSize: 4 * 1024
);var handler = new MvcChannelHandler();
// Receive buffer sizes
handler.ReceiveTextBufferSize = 8 * 1024;
handler.ReceiveBinaryBufferSize = 8 * 1024;
// Send buffer sizes
handler.SendTextBufferSize = 8 * 1024;
handler.SendBinaryBufferSize = 8 * 1024;
// Response send timeout
handler.ResponseSendTimeout = TimeSpan.FromSeconds(30);Implement the IWebSocketHandler interface to create custom handlers:
public class CustomHandler : IWebSocketHandler
{
public WebSocketHandlerMetadata Metadata { get; } = new WebSocketHandlerMetadata
{
Describe = "Custom handler",
CanHandleBinary = true,
CanHandleText = true
};
public async Task ConnectionEntry(HttpContext context, WebSocket webSocket)
{
// Handle connection logic
}
}WebSocketRouteMiddleware is the core middleware responsible for:
- Routing WebSocket requests to corresponding channels
- Managing WebSocket connection lifecycle
- Handling connection upgrades
app.UseWebSockets(webSocketOptions);
app.UseWebSocketServer();builder.Services.ConfigureWebSocketRoute(x =>
{
// Channel configuration
x.WebSocketChannels = new Dictionary<string, WebSocketRouteOption.WebSocketChannelHandler>();
// Service collection (for dependency injection)
x.ApplicationServiceCollection = builder.Services;
// Bandwidth limit policy (optional)
x.BandwidthLimitPolicy = new BandwidthLimitPolicy
{
Enabled = true,
// ... configuration
};
// Cluster configuration (optional)
x.EnableCluster = false;
});var webSocketOptions = new WebSocketOptions()
{
// Keep-Alive interval
KeepAliveInterval = TimeSpan.FromSeconds(120),
// Receive buffer size (deprecated, configure in handler)
// ReceiveBufferSize = 4 * 1024
};Requests use JSON format, following the MvcRequestScheme structure:
{
"target": "ControllerName.ActionName",
"body": {
"param1": "value1",
"param2": 123
}
}Note: The target parameter is case-insensitive.
{
"target": "WeatherForecast.Get",
"body": {}
}{
"target": "WeatherForecast.Get",
"body": {
"city": "Beijing",
"date": "2024-01-01"
}
}Responses use JSON format, following the MvcResponseScheme structure:
{
"Target": "WeatherForecast.Get",
"Status": 0,
"Msg": null,
"RequestTime": 637395762382112345,
"CompleteTime": 637395762382134526,
"Body": {
// Method return value
}
}Target: Target method of the requestStatus: Status code (0 indicates success)Msg: Error message (if any)RequestTime: Request time (Ticks)CompleteTime: Completion time (Ticks)Body: Method return value
Two message types are supported:
- Text: Text messages (JSON format)
- Binary: Binary messages
Request processing uses an ASP.NET Core-style middleware chain: register middleware with Use(...) on the route options. They are folded into a single delegate once at startup (zero per-message dispatch overhead). Each middleware may run code before/after calling next(ctx) (wrap the request), modify the request/response, or skip next to short-circuit.
services.ConfigureWebSocketRoute(options =>
{
// Wrap: measure latency around the endpoint
options.Use(async (ctx, next) =>
{
var sw = System.Diagnostics.Stopwatch.StartNew();
await next(ctx); // run downstream middleware + endpoint
logger.LogInformation("{Target} took {Ms}ms", ctx.Request?.Target, sw.ElapsedMilliseconds);
});
// Short-circuit: reject unauthorized requests; the endpoint never runs
options.Use((ctx, next) =>
{
if (!IsAuthorized(ctx.HttpContext))
{
ctx.Response = new MvcResponseScheme { Status = 1, Id = ctx.Request?.Id, Msg = "unauthorized" };
return Task.CompletedTask; // don't call next => short-circuit
}
return next(ctx);
});
});You can also implement IWebSocketMiddleware and register it with options.Use(new MyMiddleware()).
HttpContext/WebSocket/Options: connection and route infoRequest/RequestBody: the parsed request (Id/Target/Body)Response: the endpoint result; serialized and sent after the chain (JSON for the MVC channel, binary for the MessagePack channel)SuppressResponse: set true so the framework does not send a responseItems: scratch state shared across middleware for this messageReceivedData: raw inbound bytes (materialized only when middleware is registered; copied in async mode for safety)
Connection connect/disconnect are lifecycle events — use
BeforeConnectionEvent/DisconnectedEventon the route options, not the message middleware chain.
Controllers support full dependency injection:
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
private readonly IWeatherService _weatherService;
public WeatherForecastController(
ILogger<WeatherForecastController> logger,
IWeatherService weatherService)
{
_logger = logger;
_weatherService = weatherService;
}
[WebSocket]
[HttpGet]
public async Task<WeatherForecast> Get(string city)
{
return await _weatherService.GetWeatherAsync(city);
}
}Multiple parameter binding methods are supported:
[WebSocket]
public string GetMessage(string message)
{
return $"Received: {message}";
}Request:
{
"target": "Controller.GetMessage",
"body": {
"message": "Hello"
}
}public class RequestModel
{
public string Name { get; set; }
public int Age { get; set; }
}
[WebSocket]
public string Process(RequestModel model)
{
return $"Name: {model.Name}, Age: {model.Age}";
}Request:
{
"target": "Controller.Process",
"body": {
"name": "John",
"age": 30
}
}[WebSocket]
public string ProcessList(List<string> items)
{
return string.Join(", ", items);
}Request:
{
"target": "Controller.ProcessList",
"body": {
"items": ["item1", "item2", "item3"]
}
}Full support for async methods:
[WebSocket]
public async Task<string> GetAsync(string id)
{
var data = await _repository.GetAsync(id);
return data;
}Exceptions thrown by methods are caught and returned as error responses:
{
"Target": "Controller.Action",
"Status": 1,
"Msg": "Exception message",
"RequestTime": 123456789,
"CompleteTime": 123456790,
"Body": null
}Custom error handling can be implemented by wrapping the endpoint with a middleware:
options.Use(async (ctx, next) =>
{
try
{
await next(ctx);
}
catch (Exception ex)
{
// Custom error handling: replace the response
ctx.Response = new MvcResponseScheme { Status = 1, Id = ctx.Request?.Id, Msg = ex.Message };
}
});Adjust buffer sizes based on message size:
// Small messages (< 4KB)
var handler = new MvcChannelHandler(4 * 1024, 4 * 1024);
// Medium messages (4KB - 64KB)
var handler = new MvcChannelHandler(64 * 1024, 64 * 1024);
// Large messages (> 64KB)
var handler = new MvcChannelHandler(256 * 1024, 256 * 1024);Use static dictionary to manage connections:
// Get all connections
var connections = MvcChannelHandler.Clients;
// Check if connection exists
if (MvcChannelHandler.Clients.TryGetValue(connectionId, out var webSocket))
{
// Connection exists
}MvcChannelHandler supports concurrent processing of multiple requests, with each connection processed independently.
- Use Dependency Injection - Fully utilize ASP.NET Core's dependency injection system
- Async Methods - Use async methods for I/O operations to improve performance
- Error Handling - Properly handle exceptions and return meaningful error messages
- Parameter Validation - Validate parameters in methods to ensure data validity
- Logging - Use logging to record important operations and errors
For complete examples, refer to: