The WebSocketListener class provides simple methods that listen for and accept incoming WebSocket connection requests asynchronously. It is a lightweight listener with an API very similar to the System.Net.TcpListener
class.
It does not use the Microsoft's System.Net.WebSockets
namespace. It should work in any operating system running Microsoft.NET/Mono v4.5. This class is perfect for creating endpoints with WebSockets in Windows 2008 or Windows 7, which are not supported by System.Net.WebSockets
. Also works on Linux through Mono.
WebSocketListener has been designed to provide WebSocket connectivity to other applications, in the same way that System.Net.TcpListener
provides TCP connectivity. It is not a communication framework on its own and it does not provide any kind of publisher/subscriber pattern or reliable messaging beyond TCP.
wss://
(secure). More info.StreamReader
and StreamWriter
. Two different WebSocket messages, yield two different streams.Take a look on the performance and load tests on a simple 'echo' server.
This is a fork from project. There is some new features and bug fixes.
Major Features:
WebSocketClient
Transports
Minor Features:
Lost Features:
Known Problems:
WebSocketListener is available through NuGet
PM> Install-Package deniszykov.WebSocketListener
Setting up a server and start listening for clients is very similar to a TcpListener
. An listening endpoint and a WebSocket standard is the minimum needed to set up a server.
var options = new WebSocketListenerOptions();
options.Standards.RegisterRfc6455();
var server = new WebSocketListener(new IPEndPoint(IPAddress.Any, 8006), options);
await server.StartAsync();
The class vtortola.WebSockets.Rfc6455.WebSocketFactoryRfc6455
gives support to the RFC 6455, that is the WebSocket standard used at the moment. Future standards can be added in the same way.
Optionally, you can also:
options.ConnectionExtensions.RegisterSecureConnection(certificate)
options.Standards.RegisterRfc6455(f=>{f.MessageExtensions.RegisterDeflateCompression();})
Once the server has started, clients can be awaited asynchronously. When a client connects, a WebSocket
object will be returned:
var webSocket = await server.AcceptWebSocketAsync(cancellation);
The client provides means to read and write messages. With the client, as in the underlying NetworkStream
, is possible to write and read at the same time even from different threads, but is not possible to read from two or more threads at the same time, same for writing.
AcceptWebSocketAsync
should be in a loop to continuously accept new clients, also wrapped in a try/catch
since errors in the negotiation process will be thrown here. Take a look to the simple host tutorial.
⚠️ You must receive messages even if you do not need them. If you do not do this, then random disconnects are possible.
⚠️ Some synchronization mechanism is required to prevent parallel reading from one instance of WebSocket. Use SemaphoreSlim if you reading directly from WebSocket. Use async while cycle and BufferBlock if you want to form a read message queue.
With the webSocket
we can await for a message:
var messageReader = await webSocket.ReadMessageAsync(cancellationToken);
Messages are a stream-like objects, so is it possible to use regular .NET framework tools to work with them. The WebSocketMessageReadStream.MessageType
property indicates the kind of content the message contains, so it can be used to select a different handling approach.
The returned WebSocketMessageReadStream
object will contain information from the header, like type of message (Text or Binary) but not the message content, neither the message length, since a frame only contains the frame length rather than the total message length, therefore that information could be missleading.
A text message can be read with a simple StreamReader
. It is worth remember that according to the WebSockets specs, it always uses UTF8-no-BOM for text enconding:
var utf8NoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false);
if(messageReader.MessageType == WebSocketMessageType.Text)
{
String msgContent = String.Empty;
using (var reader = new StreamReader(messageReadStream, utf8NoBom))
msgContent = await reader.ReadToEndAsync();
}
ReadMessageAsync
should go in a loop, to read messages continuously. Writes and read can be performed at the same time. Take a look to the simple host tutorial.
Also, a binary message can be read using regular .NET techniques:
if(messageReader.MessageType == WebSocketMessageType.Binary)
{
using (var stream = new MemoryStream())
{
await messageReader.CopyToAsync(stream);
}
}
⚠️ Some synchronization mechanism is required to prevent parallel writing to one instance of WebSocket. Use SemaphoreSlim if you writing directly to WebSocket. Use ActionBlock if you want to form a write message queue.
Writing messages is also easy. The webSocket.CreateMessageWriter(WebSocketMessageType)
method allows to create a write only stream:
using (var messageWriter = webSocket.CreateMessageWriter(WebSocketMessageType.Text))
Once a message writer is created, regular .NET tools can be used to write in it:
using (var messageWriter = webSocket.CreateMessageWriter(WebSocketMessageType.Text))
using (var streamWriter = new StreamWriter(messageWriter, utf8NoBom))
{
await streamWriter.WriteAsync("Hello World!");
await streamWriter.FlushAsync();
await messageWriter.CloseAsync();
}
Or:
webSocket.WriteStringAsync("Hello World!");
Also binary stream messages:
using (var messageWriter = webSocket.CreateMessageWriter(WebSocketMessageType.Binary))
{
await myFileStream.CopyToAsync(messageWriter);
await messageWriter.CloseAsync();
}
Also binary messages:
using (var messageWriter = webSocket.CreateMessageWriter(WebSocketMessageType.Binary))
{
await writer.WriteAndCloseAsync(bytes, offset, count).ConfigureAwait(false);
}
Or:
webSocket.WriteBytesAsync(bytes, offset, count);
Take a look on the WebSocketListener samples.
Copyright (c) 2014 vtortola, deniszykov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.