Table of Contents

API Reference

ISession

Base interface for all session types. Contains everything needed for TCP and WebSocket sessions:

public interface ISession : IAsyncDisposable
{
    long Id { get; }                            // Unique auto-incrementing ID
    EndPoint? RemoteEndPoint { get; }           // Remote client's IP and port
    ConnectionState State { get; }              // Connected, Closing, Closed
    DisconnectReason DisconnectReason { get; }  // Why the connection was closed
    ConnectionMetrics Metrics { get; }          // BytesSent, BytesReceived, Uptime
    bool IsBackpressured { get; }               // True when send buffer is full
    IReadOnlySet<string> Groups { get; }        // Group memberships

    ValueTask SendAsync(ReadOnlyMemory<byte> data, CancellationToken ct = default);
    ValueTask CloseAsync(CancellationToken ct = default);
    void Abort();
    void JoinGroup(string group);
    void LeaveGroup(string group);

    IDictionary<string, object?> Items { get; } // Per-session user data
    T? Get<T>(SessionKey<T> key);               // Typed accessor
    void Set<T>(SessionKey<T> key, T value);    // Typed setter
}

IWebSocketSession

Extends ISession with WebSocket-specific methods (SendTextAsync). WS event handlers receive this type directly — no casting needed:

// Text frame from string
await session.SendTextAsync("Hello!");

// Text frame from pre-encoded UTF-8 (zero-copy)
await session.SendTextAsync(utf8Bytes);

// Binary frame
await session.SendAsync(binaryData);

// Close with status
await session.CloseAsync();

SessionManager

server.Sessions.Count;                              // Current count
server.Sessions.All;                                // IEnumerable<ISession>
server.Sessions.TryGet(id, out ISession? session);  // Lookup by ID
server.Sessions.BroadcastAsync(data);                // Send to all
server.Sessions.CloseAllAsync();                     // Graceful shutdown

ServerMetrics

Both StormTcpServer and StormWebSocketServer expose a Metrics property with server-wide aggregate counters. Built on System.Diagnostics.Metrics (Meter name: "StormSocket") for native OpenTelemetry/Prometheus/dotnet-counters integration.

server.Metrics.ActiveConnections;    // long  — currently connected sessions
server.Metrics.TotalConnections;     // long  — total connections since start
server.Metrics.MessagesSent;         // long  — total messages sent
server.Metrics.MessagesReceived;     // long  — total messages received
server.Metrics.BytesSentTotal;       // long  — aggregate bytes sent
server.Metrics.BytesReceivedTotal;   // long  — aggregate bytes received
server.Metrics.ErrorCount;           // long  — total errors

Instruments (System.Diagnostics.Metrics):

Name Type Unit
stormsocket.connections.total Counter<long> connections
stormsocket.connections.active UpDownCounter<long> connections
stormsocket.messages.sent Counter<long> messages
stormsocket.messages.received Counter<long> messages
stormsocket.bytes.sent Counter<long> bytes
stormsocket.bytes.received Counter<long> bytes
stormsocket.errors Counter<long> errors
stormsocket.connection.duration Histogram<double> ms
stormsocket.handshake.duration Histogram<double> ms

Note: UpDownCounter requires net7.0+. On net6.0, active connections are still available via server.Metrics.ActiveConnections property.

SessionGroup

server.Groups.Add("room", session);                  // Join
server.Groups.Remove("room", session);               // Leave
server.Groups.RemoveFromAll(session);                // Leave all (on disconnect)
server.Groups.BroadcastAsync("room", data);          // Send to room
server.Groups.MemberCount("room");                   // Count
server.Groups.GroupNames;                             // All room names

StormTcpClient

var client = new StormTcpClient(options);

// Events
client.OnConnected += async () => { };
client.OnDisconnected += async (DisconnectReason reason) => { };
client.OnDataReceived += async (ReadOnlyMemory<byte> data) => { };
client.OnError += async (Exception ex) => { };
client.OnReconnecting += async (int attempt, TimeSpan delay) => { };

// Lifecycle
await client.ConnectAsync(ct);             // Connect (with optional CancellationToken)
await client.SendAsync(data, ct);          // Send data
await client.DisconnectAsync(ct);          // Graceful disconnect
await client.DisposeAsync();               // Disconnect + cleanup

// Properties
client.State;                              // ConnectionState (Connecting, Connected, Closing, Closed)
client.Metrics.BytesSent;                  // Total bytes sent
client.Metrics.BytesReceived;              // Total bytes received
client.Metrics.Uptime;                     // Connection uptime
client.UseMiddleware(middleware);           // Add middleware

StormWebSocketClient

var ws = new StormWebSocketClient(options);

// Events
ws.OnConnected += async () => { };
ws.OnDisconnected += async (DisconnectReason reason) => { };
ws.OnMessageReceived += async (WsMessage msg) => { };
ws.OnError += async (Exception ex) => { };
ws.OnReconnecting += async (int attempt, TimeSpan delay) => { };

// Lifecycle
await ws.ConnectAsync(ct);                 // Connect + WebSocket upgrade
await ws.SendTextAsync("hello", ct);       // Send text frame (masked)
await ws.SendTextAsync(utf8Bytes, ct);     // Send pre-encoded text frame
await ws.SendAsync(binaryData, ct);        // Send binary frame (masked)
await ws.DisconnectAsync(ct);              // Send Close frame + disconnect
await ws.DisposeAsync();                   // Disconnect + cleanup

// Properties
ws.State;                                  // ConnectionState
ws.Metrics;                                // ConnectionMetrics
ws.RemoteEndPoint;                         // Server endpoint
ws.UseMiddleware(middleware);               // Add middleware

IConnectionMiddleware

public interface IConnectionMiddleware
{
    ValueTask OnConnectedAsync(ISession session);
    ValueTask<ReadOnlyMemory<byte>> OnDataReceivedAsync(ISession session, ReadOnlyMemory<byte> data);
    ValueTask<ReadOnlyMemory<byte>> OnDataSendingAsync(ISession session, ReadOnlyMemory<byte> data);
    ValueTask OnDisconnectedAsync(ISession session, DisconnectReason reason);  // Called in reverse order
    ValueTask OnErrorAsync(ISession session, Exception exception);
}

All methods have default no-op implementations.

Message Framers

Framer Format Use Case
RawFramer No framing, pass-through Raw TCP streams
LengthPrefixFramer [4-byte BE length][payload] Binary protocols
DelimiterFramer [payload][delimiter] (default: \n) Text protocols, line-based
Custom IMessageFramer Your format Custom protocols

WsMessage

public readonly struct WsMessage
{
    ReadOnlyMemory<byte> Data { get; }  // Raw payload
    bool IsText { get; }                // True = Text frame, False = Binary
    string Text { get; }                // UTF-8 decode (throws if !IsText)
}