deniszykov.WebSocketListener 4.2.16

dotnet add package deniszykov.WebSocketListener --version 4.2.16                
NuGet\Install-Package deniszykov.WebSocketListener -Version 4.2.16                
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="deniszykov.WebSocketListener" Version="4.2.16" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add deniszykov.WebSocketListener --version 4.2.16                
#r "nuget: deniszykov.WebSocketListener, 4.2.16"                
#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.
// Install deniszykov.WebSocketListener as a Cake Addin
#addin nuget:?package=deniszykov.WebSocketListener&version=4.2.16

// Install deniszykov.WebSocketListener as a Cake Tool
#tool nuget:?package=deniszykov.WebSocketListener&version=4.2.16                

Build status

WebSocketListener

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.

  • It can work with both Text or Binary messages.
  • It supports wss://(secure). More info.
  • It supports per-message deflate compression. More info.
  • It can work with multiple WebSocket standards simultaneously. More info
  • It is extensible. More info.
  • It is asynchronous.
  • It supports Mono. [More info] (//github.com/vtortola/WebSocketListener/wiki/Mono-support)
  • It supports .NET Framework 4.5 and up, NETStandard 1.3 and up and Universal Windows Platform 10 (uap10.0).
  • It has the Ping/Pong functionality built-in.
  • It can measure connection latency. More info
  • It can work with cookies and custom HTTP response statuses. More info
  • It detects and disconnects half open connections.
  • It allows to send and receive messages as streams. WebSocket messages are represented as delimited stream-like objects, that allows integration with other .NET objects like e.g. StreamReader and StreamWriter. Two different WebSocket messages, yield two different streams.
  • Messages reads and writes are streamed. Big messages are not held in memory during reads or writes.
  • It handles partial frames transparently. The WebSocket specification states that a single message can be sent across multiple individual frames. The message stream will allow to read all the message data, no matter if it was sent in a single or multiple frames.
  • It handles interleaved control frames transparently. The WebSocket specification states that control frames can appear interleaved with data frames, including between partial frames of the same message. The message stream will allow to read just the message data, it will skip the control frames.

Take a look on the performance and load tests on a simple 'echo' server.

What's new in v4 (this fork)

This is a fork from project. There is some new features and bug fixes.

Major Features:

  • WebSocketClient

    • Same features and options as WebSocketListener
    • TLS support
    • works with IPv4/IPv6
  • Transports

    • TCP, Unix Sockets, Named Pipes (why not)
    • System.Net.Socket is abstracted as NetworkConnection
    • Graceful disconnection
    • Fine tuning with WebSocketListenerOptions. Fluent confuration API.

Minor Features:

  • Fully async API (it is strongly discouraged to use synchronous API with IO operations)
  • Logging via abstract ILogger
  • No additional dependencies (ServiceModel, TPL Dataflow)
  • New Headers<HeadersT> collections with fast access to known headers
  • Custom BufferManager, and use of BufferManager in all operations with buffers
  • WebSocketListener can now listen multiple endpoints from different transports
  • Pings now processed in batches or could be manually batched and sent (WebSocket.PingAsync)
  • WebSocketMessageWriteStream now can be gracefully closed with CloseAsync()
  • Tested to work under load
  • More unit-tests

Lost Features:

  • UWP target platform (netstandard1.3 is added instead)
  • synchronous IO methods on WebSocket and Streams (Write/Read/Close)
  • Cookies collection on WebSocketHttpRequest (class is still there)
  • Removed potentially dangerous feature 'RemoveBOM' on WSWriteStream.Write and refactored WriteStringAsync(), ReadStringAsync methods to use UTF-8 without BOM

Known Problems:

  • WebSocketDeflateStream uses sync Stream methods, it should be rewritten to fully support async operations.
  • Mono can't handle IPv6 Dual Mode sockets properly (exception in Socket.RemoteEndPoint and Socket.LocalEndPoint).

Quickstart

Install

WebSocketListener is available through NuGet

PM> Install-Package deniszykov.WebSocketListener
Set up

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();

Full Code

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:

Accepting clients

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.

Receiving messages

⚠️ 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);
   }
}
Sending messages

⚠️ 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);
Example

Take a look on the WebSocketListener samples.

The MIT License (MIT)

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.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp1.0 was computed.  netcoreapp1.1 was computed.  netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard1.3 is compatible.  netstandard1.4 was computed.  netstandard1.5 was computed.  netstandard1.6 was computed.  netstandard2.0 was computed.  netstandard2.1 was computed. 
.NET Framework net45 is compatible.  net451 was computed.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen30 was computed.  tizen40 was computed.  tizen60 was computed. 
Universal Windows Platform uap was computed.  uap10.0 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on deniszykov.WebSocketListener:

Package Downloads
Buttplug.Client.Connectors.WebsocketConnector

Websocket Connection Capabilities for Buttplug Clients. (.Net Standard 2.0+)

Buttplug.Server.Connectors.WebsocketServer

Websocket Connection Capabilities for Buttplug Servers. (.Net Framework 4.7+/.Net Standard 2.0)

Xamarin.Android.V8

V8 JavaScript Engine Bindings for Xamarin.Android with Inspector Enabled

Iterum.WebSockets

WebSockets-based Network

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on deniszykov.WebSocketListener:

Repository Stars
Ulterius/server
[WIP] Ulterius™ server where all the magic happens :rocket: :feelsgood:
Version Downloads Last updated
4.2.16 4,251 5/17/2023
4.2.15 12,275 4/6/2022
4.2.14 2,589 5/15/2021
4.2.10 1,386 9/8/2020
4.2.9 1,284 7/3/2020
4.2.8-beta 582 2/27/2020
4.2.7-alpha 757 6/27/2019
4.2.6-alpha 1,253 10/29/2018
4.2.4 95,399 7/3/2018
4.2.2 5,075 3/22/2018
4.1.3 2,074 11/7/2017
4.0.0 2,530 6/21/2017

v.4.2.16
@animehunter refactored WebSocketHandshaker to support async IO operation

v.4.2.15
fixed BufferManager.CreateBufferManager should accept pool size in bytes instead of items.
fixed misleading error message when BufferManager and SendBuffer is not set properly.


v.4.2.14
fixed connection close reason parsing and hung(and timeout) after receiving close frame from client.

v.4.2.11
added close reply if remote party is requesting connection close.

v.4.2.10
fixed crash while debug logging Unix Domain Socket connection in `AcceptSocketAsConnection` method.
added additional checks for errord Socket.RemoteEndPoint and Socket.LocalEndPoint, now they produces 'WebSocketHttpRequest.NoAddress'

v.4.2.9
reverted gzip per-message compression back to original code
added WSClient gzip per-message compression negotiation

v.4.2.8
fix LocalEndPoint value on WebSocketConnectionRfc6455

v.4.2.7
[WS] added WriteAndCloseAsync to WebSocketWriteStream
[Compression] added custom async inflater/deflater in WS deflate extension. This would allow make inflation/deflation during WriteAsync/ReadAsync
[WS] replaced extra allocation of Stopwatch with less precise DateTime when measuring negotiation queue
[WS] add int overload to CloseAsync()
[WS] Expose client close codes


v.4.2.6
[WS] fixed ping timeout error on busy WebSockets

v.4.2.5
fixed error in WSWriterStream.Dispose() method
reset latency value on WS close or ping timeout
changed WSConnection Dispose() routine. Now it is not waiting for graceful close.
changed Ping timeout now cause WSConnection.Dispose instead on CloseAsync
fixed Ping timeout now properly calculated based on WS activity

v.4.2.4
[Common] replaced all DynamicMethod invocation with slower reflection alternative in case AOT-only runtime.
[WS] better error message while writing to closed stream
[Transports] clean-up SocketAsyncEventArgs after each operation to prevent holding GC references on used buffers

v.4.2.1
added DualMode option for TcpTransport
fixed race condition in WebSocket.Write() (Thanks @fuzzykiller)

v.4.2.0
stable version

v.4.1.6
fixed ping queue stop on error
added in/out buffers for ping/pong to reduce change of collision if both side pinging
replaced masking/demasking algorithm with faster unsafe implementation
added IpProtectionLevel option for tcp transport
fixed unbserved exception in WSMessageWriteStream.CloseAsync method

v.4.1.5
removed unused code from helpers
Prevent crash when value contains only empty spaces

v.4.1.4
added few changes in SendFrameAsync to prevent unobserved exceptions
added better error message for parallel AcceptWebSocketAsync call
added better error message when handshake error occurred

v.4.1.3
fixed unobserved exception at WebSocketSecureConnectionExtension.ExtendConnectionAsync

v.4.1.2
fixed infinite loop in timed queue (batch-ping related stuff)

v.4.1.1
fixed bug in BandwidthSaving ping strategy

v.4.1.0
added request header to WebSocketClient.ConnectAsync signature
PingQueue list re-use
Echo refactoring (perf. counters removed)
JetBrains.Annotations is now internal

v4.0.4
fixed TimedQueue implementation (atomic Int64 reading for 32 bit systems)
fixed Headers add NameValueCollection implementation to threat header values as not splitted headers
removed AsyncResultTask and refactored BeginRead/BeginWrite on streams
updated AsyncQueue implementation

v.4.0.3
added 'Connection:close' header to 'Bad Request' response of WebSocket handshaker
fixed Unobserved exception because wrong implementation of TaskHelper.IgnoreFault.
refactored TaskHelper.IgnoreFault and TaskHelper.PropagateResutTo to shortcut on Completed tasks
fixed ping errors on WebSockets
fixed TimedQueue impl
Headers refactoring, added HeaderFlags parameter to headers
refactored Socket shutdown sequence to prevent 'connection reset by peer' errors

UNIX Domain Sockets are tested on Linux