- Features
- Installation
- Quick Start
- Core Concepts
- Advanced Usage
- API Reference
- Inspiration
- Contributing
- License
- π Automatic Reconnection - Built-in reconnection logic with configurable strategies
- π‘ Reactive Streams - Observable sequences for messages, connections, and disconnections
- π§΅ Thread-Safe - Safe concurrent message sending and receiving
- π¦ Message Queuing - Automatic buffering with channel-based send queue
- β‘ High Performance - Built on
System.Threading.ChannelsandArrayPool<byte> - π― Type-Safe - Strong typing with text/binary message support
- π Proper Resource Management - Full
IAsyncDisposablesupport with graceful shutdown
- π₯ Multi-Client Support - Handle multiple WebSocket connections simultaneously
- π Client Tracking - Built-in client metadata and connection management
- π Event Streams - Observables for client connect/disconnect events
- π― Selective Messaging - Send to specific clients or broadcast to all
- π‘οΈ Robust Cleanup - Automatic client cleanup on disconnect
dotnet add package WebSocket.RxRequirements: .NET 10.0 or higher
using WebSocket.Rx;
using R3;
// Create and configure client
await using var client = new ReactiveWebSocketClient(new Uri("wss://echo.websocket.org"))
{
IsReconnectionEnabled = true,
KeepAliveInterval = TimeSpan.FromSeconds(30),
IsTextMessageConversionEnabled = true
};
// Subscribe to messages
client.MessageReceived
.Subscribe(msg => Console.WriteLine($"Received: {msg.Text}"));
// Subscribe to connection events
client.ConnectionHappened
.Subscribe(info => Console.WriteLine($"Connected: {info.Reason}"));
client.DisconnectionHappened
.Subscribe(info => Console.WriteLine($"Disconnected: {info.Reason}"));
// Connect and send messages
await client.StartAsync();
await client.SendAsTextAsync("Hello WebSocket!");using WebSocket.Rx;
using R3;
// Create and start server
await using var server = new ReactiveWebSocketServer("http://localhost:8080/")
{
IsTextMessageConversionEnabled = true
};
// Subscribe to client events
server.ClientConnected
.Subscribe(client => Console.WriteLine($"Client connected: {client.Metadata.Id}"));
server.Messages
.Subscribe(msg =>
{
Console.WriteLine($"From {msg.Metadata.Id}: {msg.Message.Text}");
// Echo back to sender
server.SendAsTextAsync(msg.Metadata.Id, $"Echo: {msg.Message.Text}");
});
await server.StartAsync();
Console.WriteLine($"Server running with {server.ClientCount} clients");WebSocket.Rx is built around reactive streams using R3:
// Filter and transform messages
client.MessageReceived
.Where(msg => msg.MessageType == MessageType.Text)
.Select(msg => msg.Text.ToUpper())
.Subscribe(text => Console.WriteLine(text));
// Debounce reconnection events
client.ConnectionHappened
.Throttle(TimeSpan.FromSeconds(1))
.Subscribe(info => Console.WriteLine("Stable connection established"));// Send text message (queued)
await client.SendAsTextAsync("Hello");
// Send binary message (queued)
await client.SendAsBinaryAsync(new byte[] { 0x01, 0x02 });
// Send instant (bypasses queue)
await client.SendInstantAsync("Urgent message");
// Try send (non-blocking)
bool sent = client.TrySendAsText("Optional message");// Start connection
await client.StartAsync();
// Reconnect manually
await client.ReconnectAsync();
// Stop gracefully
await client.StopAsync(WebSocketCloseStatus.NormalClosure, "Goodbye");
// Dispose (automatic cleanup)
await client.DisposeAsync();var client = new ReactiveWebSocketClient(new Uri("wss://example.com"))
{
// Connection settings
ConnectTimeout = TimeSpan.FromSeconds(10),
KeepAliveInterval = TimeSpan.FromSeconds(30),
KeepAliveTimeout = TimeSpan.FromSeconds(10),
// Reconnection
IsReconnectionEnabled = true,
// Message handling
IsTextMessageConversionEnabled = true,
MessageEncoding = Encoding.UTF8
};// Broadcast to all clients
foreach (var clientId in server.ConnectedClients.Keys)
{
await server.SendAsTextAsync(clientId, "Broadcast message");
}
// Send to specific clients
var targetClients = server.ConnectedClients
.Where(c => c.Value.CustomData?.Contains("premium") == true)
.Select(c => c.Key);
foreach (var clientId in targetClients)
{
await server.SendAsTextAsync(clientId, "Premium feature alert!");
}client.DisconnectionHappened
.Subscribe(info =>
{
Console.WriteLine($"Disconnect reason: {info.Reason}");
if (info.Exception != null)
{
Console.WriteLine($"Error: {info.Exception.Message}");
}
});// Wait for connection before sending
client.ConnectionHappened
.Take(1)
.Subscribe(_ => client.SendAsTextAsync("Connected!"));
// Process messages in batches
client.MessageReceived
.Buffer(TimeSpan.FromSeconds(1))
.Where(batch => batch.Count > 0)
.Subscribe(batch => Console.WriteLine($"Processed {batch.Count} messages"));| Property | Type | Description |
|---|---|---|
Url |
Uri |
WebSocket server URL |
IsStarted |
bool |
Client started state |
IsRunning |
bool |
Client running state |
IsReconnectionEnabled |
bool |
Enable auto-reconnect |
MessageReceived |
Observable<ReceivedMessage> |
Message stream |
ConnectionHappened |
Observable<Connected> |
Connection stream |
DisconnectionHappened |
Observable<Disconnected> |
Disconnection stream |
Key Methods:
Task StartAsync()- Start the clientTask StopAsync(status, description)- Stop gracefullyTask ReconnectAsync()- Manual reconnectTask SendAsTextAsync(message)- Send text (queued)Task SendAsBinaryAsync(data)- Send binary (queued)ValueTask DisposeAsync()- Clean up resources
| Property | Type | Description |
|---|---|---|
IsRunning |
bool |
Server running state |
ClientCount |
int |
Number of connected clients |
ConnectedClients |
IReadOnlyDictionary<Guid, Metadata> |
Client metadata |
ClientConnected |
Observable<ClientConnected> |
Client connect stream |
ClientDisconnected |
Observable<ClientDisconnected> |
Client disconnect stream |
Messages |
Observable<ServerReceivedMessage> |
Server message stream |
Key Methods:
Task StartAsync()- Start the serverTask<bool> StopAsync(status, description)- Stop serverTask<bool> SendAsTextAsync(clientId, message)- Send to clientValueTask DisposeAsync()- Clean up resources
This library is inspired by and builds upon the excellent work of:
Websocket.Client by Marfusios
WebSocket.Rx takes inspiration from Websocket.Client's elegant reactive approach to WebSocket communication. Key influences include:
- Reactive-First Design - Using observables for all events and messages
- Automatic Reconnection - Built-in reconnection logic for robust connections
- Clean API - Intuitive and easy-to-use interface
While honoring the spirit of Websocket.Client, WebSocket.Rx offers:
- β R3 Integration - Built on the modern R3 reactive library (successor to Rx.NET)
- β Server Support - Full-featured WebSocket server implementation
- β Modern .NET - Built for .NET 10+ with latest performance optimizations
- β IAsyncDisposable - Proper async resource cleanup
- β
Channel-Based Queuing - High-performance message queue using
System.Threading.Channels - β
Enhanced Memory Management - Uses
ArrayPool<byte>andRecyclableMemoryStream
Both libraries share the same core philosophy: WebSocket communication should be simple, reactive, and reliable.
Contributions are welcome! This library grows with the community's needs.
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Write tests for your changes
- Ensure all tests pass:
dotnet test - Submit a Pull Request
- β Follow existing code style and conventions
- β Include unit tests for new features
- β Update documentation for API changes
- β Keep PRs focused and atomic
- β Write meaningful commit messages
git clone https://github.com/st0o0/WebSocket.Rx.git
cd WebSocket.Rx
dotnet restore
dotnet build
dotnet testThis project is licensed under the MIT License - see the LICENSE file for details.
Built with β€οΈ for the .NET community
