RepletoryLib.WebSocket.Abstractions 1.0.0

dotnet add package RepletoryLib.WebSocket.Abstractions --version 1.0.0
                    
NuGet\Install-Package RepletoryLib.WebSocket.Abstractions -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RepletoryLib.WebSocket.Abstractions" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RepletoryLib.WebSocket.Abstractions" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="RepletoryLib.WebSocket.Abstractions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RepletoryLib.WebSocket.Abstractions --version 1.0.0
                    
#r "nuget: RepletoryLib.WebSocket.Abstractions, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RepletoryLib.WebSocket.Abstractions@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RepletoryLib.WebSocket.Abstractions&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=RepletoryLib.WebSocket.Abstractions&version=1.0.0
                    
Install as a Cake Tool

RepletoryLib.WebSocket.Abstractions

WebSocket hub abstractions and interfaces for real-time connection management, group messaging, broadcast, and message handling.

Part of the RepletoryLib ecosystem -- standalone, reusable .NET 10 libraries with zero business logic.

NuGet .NET 10 License: MIT


Overview

RepletoryLib.WebSocket.Abstractions defines the core contracts, models, enums, and options for real-time WebSocket communication. It provides IWebSocketHub for sending messages to connections, users, and groups, IWebSocketConnectionManager for tracking connection lifecycle and group memberships, and IWebSocketMessageHandler for pluggable message routing based on message type.

This package contains no implementations -- it is the abstraction layer that concrete providers (in-memory, Redis-backed, etc.) implement. Reference this package in your application code to program against interfaces, then swap providers without changing business logic.

Key Features

  • IWebSocketHub -- Core hub contract: send to connection, send to user, broadcast to all, broadcast to group, manage group membership, and disconnect
  • IWebSocketConnectionManager -- Connection lifecycle management: add, remove, query by ID/user/group, group membership, and connection counting
  • IWebSocketMessageHandler -- Pluggable message handler matched by message type string for custom message processing
  • WebSocketMessage -- Message model with type, payload, sender info, timestamp, and optional metadata
  • WebSocketConnection -- Connection model tracking identity, state, group memberships, and activity timestamps
  • SendMessageRequest / BroadcastRequest / GroupBroadcastRequest -- Typed request models for targeted, broadcast, and group-scoped messaging
  • SendResult / BulkSendResult -- Result types with per-connection success/failure tracking and static factory methods
  • WebSocketOptions -- Shared configuration for heartbeat intervals, connection limits, message sizes, allowed origins, and endpoint path
  • ConnectionState enum -- Connection lifecycle states: Connecting, Connected, Disconnecting, Disconnected
  • WebSocketMessageType enum -- Message type classifications: Text, System, Ping, Pong, Connected, Disconnected, GroupJoined, GroupLeft, Error

Installation

dotnet add package RepletoryLib.WebSocket.Abstractions

Or add to your .csproj:

<PackageReference Include="RepletoryLib.WebSocket.Abstractions" Version="1.0.0" />

Note: RepletoryLib packages are published to a local BaGet feed. See the main repository README for feed configuration.

Dependencies

Package Type
RepletoryLib.Common RepletoryLib

Quick Start

1. Reference the abstractions in your service layer

using RepletoryLib.WebSocket.Abstractions.Interfaces;
using RepletoryLib.WebSocket.Abstractions.Models;

public class ChatService
{
    private readonly IWebSocketHub _hub;

    public ChatService(IWebSocketHub hub) => _hub = hub;

    public async Task SendDirectMessageAsync(string connectionId, string text)
    {
        var result = await _hub.SendToConnectionAsync(new SendMessageRequest
        {
            ConnectionId = connectionId,
            Message = new WebSocketMessage
            {
                Type = "Chat",
                Payload = text
            }
        });

        if (!result.Success)
            throw new InvalidOperationException($"Send failed: {result.Error}");
    }
}

2. Register a concrete provider in Program.cs

// In-memory provider (from RepletoryLib.WebSocket.Core)
using RepletoryLib.WebSocket.Core;
builder.Services.AddRepletoryWebSocket(builder.Configuration);

Configuration

WebSocketOptions

Property Type Default Description
HeartbeatIntervalSeconds int 30 Interval in seconds between heartbeat ping/pong messages
MaxConnections int 10000 Maximum number of concurrent WebSocket connections
MaxMessageSizeBytes int 65536 (64 KB) Maximum allowed size for a single WebSocket message
ConnectionTimeoutSeconds int 120 Seconds of inactivity before a connection is considered timed out
AllowedOrigins List<string> [] (all allowed) Allowed origins for WebSocket requests. Empty list permits all
EndpointPath string "/ws" URL path the WebSocket endpoint listens on

Section name: "WebSocket"

{
  "WebSocket": {
    "HeartbeatIntervalSeconds": 30,
    "MaxConnections": 10000,
    "MaxMessageSizeBytes": 65536,
    "ConnectionTimeoutSeconds": 120,
    "AllowedOrigins": ["https://example.com", "https://app.example.com"],
    "EndpointPath": "/ws"
  }
}

Usage Examples

Send a Message to a Specific Connection

using RepletoryLib.WebSocket.Abstractions.Interfaces;
using RepletoryLib.WebSocket.Abstractions.Models;

public class NotificationService
{
    private readonly IWebSocketHub _hub;

    public NotificationService(IWebSocketHub hub) => _hub = hub;

    public async Task NotifyConnectionAsync(string connectionId, string eventType, string payload)
    {
        var result = await _hub.SendToConnectionAsync(new SendMessageRequest
        {
            ConnectionId = connectionId,
            Message = new WebSocketMessage
            {
                Type = eventType,
                Payload = payload,
                Metadata = new Dictionary<string, string>
                {
                    ["source"] = "notification-service"
                }
            }
        });

        if (!result.Success)
            Console.WriteLine($"Failed to send to {connectionId}: {result.Error}");
    }
}

Send to All Connections for a User

public async Task NotifyUserAsync(string userId, string message)
{
    var result = await _hub.SendToUserAsync(userId, new WebSocketMessage
    {
        Type = "Notification",
        Payload = message
    });

    if (!result.Success)
        Console.WriteLine($"Failed to notify user {userId}: {result.Error}");
}

Broadcast to All Connections

public async Task BroadcastAnnouncementAsync(string announcement, string? excludeConnectionId = null)
{
    var request = new BroadcastRequest
    {
        Message = new WebSocketMessage
        {
            Type = "Announcement",
            Payload = announcement
        },
        ExcludeConnectionIds = excludeConnectionId is not null
            ? [excludeConnectionId]
            : null
    };

    var result = await _hub.BroadcastAsync(request);

    Console.WriteLine($"Broadcast: {result.SuccessCount} delivered, {result.FailureCount} failed");
}

Broadcast to a Group

public async Task SendToRoomAsync(string roomName, string senderConnectionId, string message)
{
    var result = await _hub.BroadcastToGroupAsync(new GroupBroadcastRequest
    {
        GroupName = roomName,
        Message = new WebSocketMessage
        {
            Type = "Chat",
            Payload = message,
            ConnectionId = senderConnectionId
        },
        ExcludeConnectionIds = [senderConnectionId] // Don't echo back to sender
    });
}

Group Management

// Add a connection to a group
await _hub.AddToGroupAsync(connectionId, "room-general");

// Remove a connection from a group
await _hub.RemoveFromGroupAsync(connectionId, "room-general");

// Forcefully disconnect a connection
await _hub.DisconnectAsync(connectionId);

Query Connections with IWebSocketConnectionManager

using RepletoryLib.WebSocket.Abstractions.Interfaces;
using RepletoryLib.WebSocket.Abstractions.Models;

public class AdminService
{
    private readonly IWebSocketConnectionManager _connections;

    public AdminService(IWebSocketConnectionManager connections) => _connections = connections;

    public async Task<int> GetOnlineCountAsync()
    {
        return await _connections.GetConnectionCountAsync();
    }

    public async Task<IReadOnlyList<WebSocketConnection>> GetUserSessionsAsync(string userId)
    {
        return await _connections.GetConnectionsByUserAsync(userId);
    }

    public async Task<IReadOnlyList<string>> GetGroupsAsync(string connectionId)
    {
        return await _connections.GetGroupsForConnectionAsync(connectionId);
    }
}

Implement a Custom Message Handler

using RepletoryLib.WebSocket.Abstractions.Interfaces;
using RepletoryLib.WebSocket.Abstractions.Models;

public class ChatMessageHandler : IWebSocketMessageHandler
{
    public string MessageType => "Chat";

    private readonly IWebSocketHub _hub;

    public ChatMessageHandler(IWebSocketHub hub) => _hub = hub;

    public async Task HandleAsync(WebSocketMessage message, WebSocketConnection connection, CancellationToken cancellationToken = default)
    {
        // Echo the message back to all connections in the sender's groups
        foreach (var group in connection.Groups)
        {
            await _hub.BroadcastToGroupAsync(new GroupBroadcastRequest
            {
                GroupName = group,
                Message = message,
                ExcludeConnectionIds = [connection.ConnectionId]
            }, cancellationToken);
        }
    }
}

// Register in DI
builder.Services.AddSingleton<IWebSocketMessageHandler, ChatMessageHandler>();

API Reference

Interfaces

IWebSocketHub
Method Returns Description
SendToConnectionAsync(request, ct) Task<SendResult> Send a message to a specific connection
SendToUserAsync(userId, message, ct) Task<SendResult> Send a message to all connections for a user
BroadcastAsync(request, ct) Task<BulkSendResult> Broadcast a message to all connections with optional exclusions
BroadcastToGroupAsync(request, ct) Task<BulkSendResult> Broadcast a message to a group with optional exclusions
AddToGroupAsync(connectionId, groupName, ct) Task Add a connection to a group
RemoveFromGroupAsync(connectionId, groupName, ct) Task Remove a connection from a group
DisconnectAsync(connectionId, ct) Task Forcefully disconnect a connection
IWebSocketConnectionManager
Method Returns Description
AddConnectionAsync(connectionId, userId, ct) Task<WebSocketConnection> Register a new connection
RemoveConnectionAsync(connectionId, ct) Task Remove a connection and clean up resources
GetConnectionAsync(connectionId, ct) Task<WebSocketConnection?> Get a connection by its ID
GetConnectionsByUserAsync(userId, ct) Task<IReadOnlyList<WebSocketConnection>> Get all connections for a user
GetConnectionsByGroupAsync(groupName, ct) Task<IReadOnlyList<WebSocketConnection>> Get all connections in a group
GetAllConnectionsAsync(ct) Task<IReadOnlyList<WebSocketConnection>> Get all active connections
AddToGroupAsync(connectionId, groupName, ct) Task Add a connection to a group
RemoveFromGroupAsync(connectionId, groupName, ct) Task Remove a connection from a group
GetGroupsForConnectionAsync(connectionId, ct) Task<IReadOnlyList<string>> Get all groups a connection belongs to
GetConnectionCountAsync(ct) Task<int> Get the total number of active connections
IWebSocketMessageHandler
Property/Method Returns Description
MessageType string The message type this handler processes
HandleAsync(message, connection, ct) Task Process an incoming message within the context of its connection

Models

WebSocketMessage
Property Type Required Description
Type string Yes Message type identifier for routing to handlers
Payload string Yes Serialized message payload content
SenderId string? No User ID of the sender (null for system messages)
ConnectionId string? No Connection ID of the sender (null for system messages)
Timestamp DateTime No UTC creation timestamp (defaults to DateTime.UtcNow)
Metadata Dictionary<string, string>? No Optional key-value metadata
WebSocketConnection
Property Type Default Description
ConnectionId string (required) Unique connection identifier
UserId string? null Authenticated user ID (null for anonymous)
ConnectedAt DateTime DateTime.UtcNow UTC timestamp when connection was established
LastActivityAt DateTime DateTime.UtcNow UTC timestamp of most recent activity
State ConnectionState Connected Current connection state
Groups List<string> [] Groups this connection belongs to
Metadata Dictionary<string, string>? null Optional key-value metadata
SendMessageRequest
Property Type Required Description
ConnectionId string Yes Target connection identifier
Message WebSocketMessage Yes The message to send
BroadcastRequest
Property Type Required Description
Message WebSocketMessage Yes The message to broadcast
ExcludeConnectionIds List<string>? No Connection IDs to exclude from the broadcast
GroupBroadcastRequest
Property Type Required Description
GroupName string Yes Target group name
Message WebSocketMessage Yes The message to broadcast to the group
ExcludeConnectionIds List<string>? No Connection IDs to exclude from the broadcast
SendResult
Property Type Description
Success bool Whether the send operation succeeded
Error string? Error message (null when successful)
ConnectionId string? Connection ID the message was sent to
Static Method Returns Description
Succeeded(connectionId?) SendResult Creates a successful result
Failed(error, connectionId?) SendResult Creates a failed result with error message
BulkSendResult
Property Type Description
Results List<SendResult> Individual results for each targeted connection
SuccessCount int Number of successful sends (computed)
FailureCount int Number of failed sends (computed)

Enums

ConnectionState
Value Description
Connecting Connection is being established
Connected Connection is active and ready for messaging
Disconnecting Connection is being closed gracefully
Disconnected Connection has been fully closed
WebSocketMessageType
Value Description
Text Standard text message between clients or client-to-server
System System-generated message for internal notifications
Ping Keep-alive ping to verify connection is active
Pong Response to a Ping confirming connection is alive
Connected Client has established a WebSocket connection
Disconnected Client has disconnected from the server
GroupJoined Connection has joined a group
GroupLeft Connection has left a group
Error An error occurred during message processing

Options

WebSocketOptions
Property Type Default Description
HeartbeatIntervalSeconds int 30 Interval between heartbeat ping/pong messages
MaxConnections int 10000 Maximum concurrent connections
MaxMessageSizeBytes int 65536 Maximum message size in bytes
ConnectionTimeoutSeconds int 120 Inactivity timeout in seconds
AllowedOrigins List<string> [] Allowed origins (empty allows all)
EndpointPath string "/ws" WebSocket endpoint path

Integration with Other RepletoryLib Packages

Package Relationship
RepletoryLib.Common Direct dependency -- shared base types
RepletoryLib.WebSocket.Core In-memory implementation of IWebSocketHub, IWebSocketConnectionManager, middleware, and heartbeat service

Testing

using NSubstitute;
using RepletoryLib.WebSocket.Abstractions.Interfaces;
using RepletoryLib.WebSocket.Abstractions.Models;

public class ChatServiceTests
{
    [Fact]
    public async Task SendDirectMessageAsync_sends_to_connection()
    {
        // Arrange
        var hub = Substitute.For<IWebSocketHub>();
        hub.SendToConnectionAsync(Arg.Any<SendMessageRequest>(), Arg.Any<CancellationToken>())
            .Returns(SendResult.Succeeded("conn-123"));

        var service = new ChatService(hub);

        // Act
        await service.SendDirectMessageAsync("conn-123", "Hello!");

        // Assert
        await hub.Received(1).SendToConnectionAsync(
            Arg.Is<SendMessageRequest>(r =>
                r.ConnectionId == "conn-123" &&
                r.Message.Type == "Chat" &&
                r.Message.Payload == "Hello!"),
            Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task SendDirectMessageAsync_throws_on_failure()
    {
        // Arrange
        var hub = Substitute.For<IWebSocketHub>();
        hub.SendToConnectionAsync(Arg.Any<SendMessageRequest>(), Arg.Any<CancellationToken>())
            .Returns(SendResult.Failed("Connection not found"));

        var service = new ChatService(hub);

        // Act & Assert
        await Assert.ThrowsAsync<InvalidOperationException>(
            () => service.SendDirectMessageAsync("conn-999", "Hello!"));
    }
}

Troubleshooting

Issue Solution
SendResult.Success is false Check the Error property for the failure reason; common causes are closed connections or missing connection IDs
No handler found for message type Register an IWebSocketMessageHandler implementation in DI whose MessageType matches the incoming message's Type
BulkSendResult shows partial failures Inspect individual Results entries -- broadcast operations continue on per-connection failure
Connection not tracked Ensure the connection was added via IWebSocketConnectionManager.AddConnectionAsync before querying
User has no connections The user may have disconnected; check GetConnectionsByUserAsync returns an empty list rather than null

License

This project is licensed under the MIT License.

Copyright (c) 2024-2026 Repletory.


For complete documentation, infrastructure setup, and configuration reference, see the RepletoryLib main repository.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on RepletoryLib.WebSocket.Abstractions:

Package Downloads
RepletoryLib.WebSocket.Core

WebSocket hub implementation with connection tracking, groups, and broadcast for RepletoryLib

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 83 3/2/2026