WebSockets
An extension package enables the use of Channels over WebSockets. This extension provides support for mapping ASP.NET Core WebSocket endpoints to channel configurations. For non-ASP.NET Core applications, the extension provides a factory to create a channel from a System.Net.WebSockets.WebSocket instance.
Considerations
Unlike TCP or UDP channels, the extension library does not provide a low-level server to handle WebSocket connections. Instead, it provides a factory to create a channel on top of an existing WebSocket connection.
Also unlike the TCP and UDP channels, a WebSocket channel does not deliver raw byte buffers to the input pipeline. Instead, it delivers a WebSocketMessage object, which contains the message type and payload, because WebSockets support both binary and text messages.
Tip
Messages delivered to the input pipeline are always complete messages (EndOfMessage == true). If a fragmented message is received, the channel will buffer the fragments until it is complete and only then deliver it to the input pipeline.
When writing to the output pipeline, the library provides built-in middleware for sending binary, text or fragmented messages, so either of the following types can be directly written to the output pipeline:
byte[]which is sent as a (complete) binary messageIByteBufferalso sent as a (complete) binary messagestringwhich is sent as a (complete) text message (utf-8 encoded)WebSocketMessagewhich gives full control over the message type and content
Usage
To use this extension, add the NuGet package to your project:
The channel pipeline configuration is done in the same way as with the TCP and UDP channels. WebSocket channels use the same pipeline configuration as TCP and UDP channels. However, additional services are required to enable the WebSocket middleware.
IServiceCollection services = ...;
/*
Configure the channel or channels as usual
*/
services.AddChannels( ... );
/*
register web sockets middleware services
*/
services.AddWebSocketChannels();
Once the middleware is registered, bind the WebSocket endpoint to a channel using route mapping:
WebApplication app = ...;
// required: ASP.NET WebSockets middleware
app.UseWebSockets();
// map WebSocket endpoint to the default channel configuration
app.MapWebSocketChannel( "/ws" );
Multiple Endpoints
By using named channels, it is possible to have multiple WebSocket endpoints with different channel configurations. The following example demonstrates how to set up two named channels with different middleware:
IServiceCollection services = ...;
services.AddChannels()
.Add( "foo_channel", channel =>
{
// set up input pipeline
channel.AddInputAdapter<ExampleAdapter>()
.AddInputHandler<ExampleHandler1>();
// set up output pipeline
channel.AddOutputAdapter<ExampleAdapter>();
} )
.Add( "bar_channel", channel =>
{
// set up input pipeline
channel.AddInputAdapter<ExampleAdapter>()
.AddInputHandler<ExampleHandler2>();
// set up output pipeline
channel.AddOutputAdapter<ExampleAdapter>();
} );
To bind the named channels to the WebSocket endpoints, we just need to include the channel name in the route mapping:
WebApplication app = ...;
// required: ASP.NET WebSockets middleware
app.UseWebSockets();
// map the WebSocket endpoints
app.MapWebSocketChannel( "/ws/foo", "foo_channel" );
app.MapWebSocketChannel( "/ws/bar", "bar_channel" );
Usage without ASP.NET Core
If not using ASP.NET Core, we can still use the extension library with any HTTP server that can produce a System.Net.WebSockets.WebSocket instance. In this case, we won't be using the MapWebSocketChannel extension method, but instead, we will need to manually create the channel by using the factory.
WebSocket webSocket = ...;
// get the factory from the DI container (usually injected)
IWebSocketChannelFactory factory = ...;
// optional: graceful shutdown if using the WaitAsync method
CancellationToken cancellationToken = ...;
// create named channel using the pre-configured "channel1" pipeline
var channel = factory.CreateChannel( webSocket, "channel1" );
// optional: wait until the channel is closed or cancellation token is triggered
try
{
await channel.WaitAsync( cancellationToken );
}
catch ( OperationCanceledException )
{ }
// recommended: close the channel and release resources
await channel.CloseAsync();