![]() |
VOOZH | about |
dotnet add package VIEApps.Components.WebSockets --version 10.10.2606.1
NuGet\Install-Package VIEApps.Components.WebSockets -Version 10.10.2606.1
<PackageReference Include="VIEApps.Components.WebSockets" Version="10.10.2606.1" />
<PackageVersion Include="VIEApps.Components.WebSockets" Version="10.10.2606.1" />Directory.Packages.props
<PackageReference Include="VIEApps.Components.WebSockets" />Project file
paket add VIEApps.Components.WebSockets --version 10.10.2606.1
#r "nuget: VIEApps.Components.WebSockets, 10.10.2606.1"
#:package VIEApps.Components.WebSockets@10.10.2606.1
#addin nuget:?package=VIEApps.Components.WebSockets&version=10.10.2606.1Install as a Cake Addin
#tool nuget:?package=VIEApps.Components.WebSockets&version=10.10.2606.1Install as a Cake Tool
A concrete implementation of the System.Net.WebSockets.WebSocket abstract, that allows you to make WebSocket connections as a client or to respond to WebSocket requests as a server (or wrap existing WebSocket connections of ASP.NET / ASP.NET Core).
The class ManagedWebSocket is an implementation or a wrapper of the System.Net.WebSockets.WebSocket abstract class, that allows you send and receive messages in the same way for both side of client and server role.
async Task ReceiveAsync(ManagedWebSocket websocket)
{
var buffer = new ArraySegment<byte>(new byte[1024]);
while (true)
{
WebSocketReceiveResult result = await websocket.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false);
switch (result.MessageType)
{
case WebSocketMessageType.Close:
return;
case WebSocketMessageType.Text:
case WebSocketMessageType.Binary:
var value = Encoding.UTF8.GetString(buffer, result.Count);
Console.WriteLine(value);
break;
}
}
}
async Task SendAsync(ManagedWebSocket websocket)
{
var buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World"));
await websocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false);
}
// the identity of the connection
public Guid ID { get; }
// true if the connection was made when connect to a remote endpoint (mean client role)
public bool IsClient { get; }
// original requesting URI of the connection
public Uri RequestUri { get; }
// the time when the connection is established
public DateTime Timestamp { get; }
// the remote endpoint
public EndPoint RemoteEndPoint { get; }
// the local endpoint
public EndPoint LocalEndPoint { get; }
// Extra information
public Dictionary<string, object> Extra { get; }
// Headers information
public Dictionary<string, string> Headers { get; }
This is a centralized element for working with both side of client and server role. This class has 04 action properties (event handlers) to take care of all working cases, you just need to assign your code to cover its.
// fire when got any error
Action<ManagedWebSocket, Exception> OnError;
// fire when a connection is established
Action<ManagedWebSocket> OnConnectionEstablished;
// fire when a connection is broken
Action<ManagedWebSocket> OnConnectionBroken;
// fire when a message is received
Action<ManagedWebSocket, WebSocketReceiveResult, byte[]> OnMessageReceived;
Example:
var websocket = new WebSocket
{
OnError = (webSocket, exception) =>
{
// your code to handle error
},
OnConnectionEstablished = (webSocket) =>
{
// your code to handle established connection
},
OnConnectionBroken = (webSocket) =>
{
// your code to handle broken connection
},
OnMessageReceived = (webSocket, result, data) =>
{
// your code to handle received message
}
};
And this class has some methods for working on both side of client and server role:
void Connect(Uri uri, WebSocketOptions options, Action<ManagedWebSocket> onSuccess, Action<Exception> onFailure);
void StartListen(int port, X509Certificate2 certificate, Action onSuccess, Action<Exception> onFailure, Func<ManagedWebSocket, byte[]> getPingPayload, Func<ManagedWebSocket, byte[], byte[]> getPongPayload, Action<ManagedWebSocket, byte[]> onPong);
void StopListen();
Use the Connect method to connect to a remote endpoint
Use the StartListen method to start the listener to listen incoming connection requests.
Use the StopListen method to stop the listener.
Enabling secure connections requires two things:
var websocket = new WebSocket
{
Certificate = new X509Certificate2("my-certificate.pfx")
// Certificate = new X509Certificate2("my-certificate.pfx", "cert-password", X509KeyStorageFlags.UserKeySet)
};
websocket.StartListen();
Want to have a free SSL certificate? Take a look at Let's Encrypt.
Special: A simple tool named win-acme will help your IIS works with Let's Encrypt very well.
To enable negotiation of subprotocols, specify the supported protocols on SupportedSubProtocols property. The negotiated subprotocol will be available on the socket's SubProtocol.
If no supported subprotocols are found on the client request (Sec-WebSocket-Protocol), the listener will raises the SubProtocolNegotiationFailedException exception.
var websocket = new WebSocket
{
SupportedSubProtocols = new[] { "messenger", "chat" }
};
websocket.StartListen();
The Nagle's Algorithm is disabled by default (to send a message immediately). If you want to enable the Nagle's Algorithm, set NoDelay to false
var websocket = new WebSocket
{
NoDelay = false
};
websocket.StartListen();
When integrate this component with your app that hosted by ASP.NET / ASP.NET Core, you might want to use the WebSocket connections of ASP.NET / ASP.NET Core directly, then the method WrapAsync is here to help. This method will return a task that run a process for receiving messages from this WebSocket connection.
Task WrapAsync(System.Net.WebSockets.WebSocket webSocket, Uri requestUri, EndPoint remoteEndPoint, EndPoint localEndPoint, Dictionary<string, string> headers, Action<ManagedWebSocket> onSuccess);
And might be you need an extension method to wrap an existing WebSocket connection, then take a look at some lines of code below:
ASP.NET
public static Task WrapAsync(this net.vieapps.Components.WebSockets.WebSocket websocket, AspNetWebSocketContext context)
{
var serviceProvider = (IServiceProvider)HttpContext.Current;
var httpWorker = serviceProvider?.GetService<HttpWorkerRequest>();
var remoteAddress = httpWorker == null ? context.UserHostAddress : httpWorker.GetRemoteAddress();
var remotePort = httpWorker == null ? 0 : httpWorker.GetRemotePort();
var remoteEndpoint = IPAddress.TryParse(remoteAddress, out IPAddress ipAddress)
? new IPEndPoint(ipAddress, remotePort > 0 ? remotePort : context.RequestUri.Port) as EndPoint
: new DnsEndPoint(context.UserHostName, remotePort > 0 ? remotePort : context.RequestUri.Port) as EndPoint;
var localAddress = httpWorker == null ? context.RequestUri.Host : httpWorker.GetLocalAddress();
var localPort = httpWorker == null ? 0 : httpWorker.GetLocalPort();
var localEndpoint = IPAddress.TryParse(localAddress, out ipAddress)
? new IPEndPoint(ipAddress, localPort > 0 ? localPort : context.RequestUri.Port) as EndPoint
: new DnsEndPoint(context.RequestUri.Host, localPort > 0 ? localPort : context.RequestUri.Port) as EndPoint;
return websocket.WrapAsync(context.WebSocket, context.RequestUri, remoteEndpoint, localEndpoint);
}
ASP.NET Core
public static async Task WrapAsync(this net.vieapps.Components.WebSockets.WebSocket websocket, HttpContext context)
{
if (context.WebSockets.IsWebSocketRequest)
{
var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
var requestUri = new Uri($"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}{context.Request.PathBase}{context.Request.QueryString}");
var remoteEndPoint = new IPEndPoint(context.Connection.RemoteIpAddress, context.Connection.RemotePort);
var localEndPoint = new IPEndPoint(context.Connection.LocalIpAddress, context.Connection.LocalPort);
await websocket.WrapAsync(webSocket, requestUri, remoteEndPoint, localEndPoint).ConfigureAwait(false);
}
}
While working with ASP.NET Core, we think that you need a middle-ware to handle all request of WebSocket connections, just look like this:
public class WebSocketMiddleware
{
readonly RequestDelegate _next;
net.vieapps.Components.WebSockets.WebSocket _websocket;
public WebSocketMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
var logger = loggerFactory.CreateLogger<WebSocketMiddleware>();
this._websocket = new net.vieapps.Components.WebSockets.WebSocket(loggerFactory)
{
OnError = (websocket, exception) =>
{
logger.LogError(exception, $"Got an error: {websocket?.ID} @ {websocket?.RemoteEndPoint} => {exception.Message}");
},
OnConnectionEstablished = (websocket) =>
{
logger.LogDebug($"Connection is established: {websocket.ID} @ {websocket.RemoteEndPoint}");
},
OnConnectionBroken = (websocket) =>
{
logger.LogDebug($"Connection is broken: {websocket.ID} @ {websocket.RemoteEndPoint}");
},
OnMessageReceived = (websocket, result, data) =>
{
var message = result.MessageType == System.Net.WebSockets.WebSocketMessageType.Text ? data.GetString() : "(binary message)";
logger.LogDebug($"Got a message: {websocket.ID} @ {websocket.RemoteEndPoint} => {message}");
}
};
this._next = next;
}
public async Task Invoke(HttpContext context)
{
await this._websocket.WrapAsync(context).ConfigureAwait(false);
await this._next.Invoke(context).ConfigureAwait(false);
}
}
And remember to tell APS.NET Core uses your middleware (at Configure method of Startup.cs)
app.UseWebSockets();
app.UseMiddleware<WebSocketMiddleware>();
Messages are received automatically via parallel tasks, and you only need to assign OnMessageReceived event for handling its.
Sending messages are the same as ManagedWebSocket, with a little different: the first argument - you need to specify a WebSocket connection (by an identity) for sending your messages.
Task SendAsync(Guid id, ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken);
Task SendAsync(Guid id, string message, bool endOfMessage, CancellationToken cancellationToken);
Task SendAsync(Guid id, byte[] message, bool endOfMessage, CancellationToken cancellationToken);
Task SendAsync(Func<ManagedWebSocket, bool> predicate, ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken);
Task SendAsync(Func<ManagedWebSocket, bool> predicate, string message, bool endOfMessage, CancellationToken cancellationToken);
Task SendAsync(Func<ManagedWebSocket, bool> predicate, byte[] message, bool endOfMessage, CancellationToken cancellationToken);
Take a look at some methods GetWebSocket... to work with all connections.
ManagedWebSocket GetWebSocket(Guid id);
IEnumerable<ManagedWebSocket> GetWebSockets(Func<ManagedWebSocket, bool> predicate);
bool CloseWebSocket(Guid id, WebSocketCloseStatus closeStatus, string closeStatusDescription);
bool CloseWebSocket(ManagedWebSocket websocket, WebSocketCloseStatus closeStatus, string closeStatusDescription);
Our prefers:
using net.vieapps.Components.Utility;
using net.vieapps.Components.WebSockets;
While working directly with this component, performance is not your problem, but when you wrap WebSocket connections of ASP.NET or ASP.NET Core (with IIS Integration), may be you reach max 5,000 concurrent connections (because IIS allows 5,000 CCU by default).
ASP.NET and IIS scale very well, but you'll need to change a few settings to set up your server for lots of concurrent connections, as opposed to lots of requests per second.
Increase the number of concurrent requests IIS will serve at once:
Example:
appcmd.exe set config /section:system.webserver/serverRuntime /appConcurrentRequestLimit:100000
By default ASP.NET 4.0 sets the maximum concurrent connections to 5000 per CPU. If you need more concurrent connections then you need to increase the maxConcurrentRequestsPerCPU setting.
Open %windir%\Microsoft.NET\Framework\v4.0.30319\aspnet.config (Framework64 for 64 bit processes)
Copy from the sample below (ensure case is correct!)
Example:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="false" />
<legacyImpersonationPolicy enabled="true"/>
<alwaysFlowImpersonationPolicy enabled="false"/>
<SymbolReadingPolicy enabled="1" />
<shadowCopyVerifyByTimestamp enabled="true"/>
</runtime>
<startup useLegacyV2RuntimeActivationPolicy="true" />
<system.web>
<applicationPool maxConcurrentRequestsPerCPU="20000" />
</system.web>
</configuration>
When the total amount of connections exceed the maxConcurrentRequestsPerCPU setting (i.e. maxConcurrentRequestsPerCPU * number of logical processors), ASP.NET will start throttling requests using a queue. To control the size of the queue, you can tweak the requestQueueLimit.
Example:
<processModel autoConfig="false" requestQueueLimit="250000" />
The following performance counters may be useful to watch while conducting concurrency testing and adjusting the settings detailed above:
Memory
ASP.NET
CPU
TCP/IP
Web Service
Threading
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 4 NuGet packages that depend on VIEApps.Components.WebSockets:
| Package | Downloads |
|---|---|
|
VIEApps.Components.Utility.AspNetCore
The general purpose components for developing apps with ASP.NET Core |
|
|
VIEApps.Services.Base
The base of all microservices in the VIEApps NGX |
|
|
VIEApps.Components.Utility.AspNet
The general purpose library for developing apps with ASP.NET |
|
|
AltV.Net.NetworkingEntity
Package Description |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 10.10.2606.1 | 638 | 5/30/2026 | |
| 10.10.2605.2 | 639 | 5/24/2026 | |
| 10.10.2605.1 | 629 | 4/30/2026 | |
| 10.10.2604.24 | 649 | 4/15/2026 | |
| 10.10.2604.2 | 659 | 4/2/2026 | |
| 10.10.2604.1 | 631 | 4/2/2026 | 10.10.2604.1 is deprecated because it has critical bugs. |
| 10.10.2603.7 | 629 | 3/17/2026 | |
| 10.10.2603.6 | 649 | 3/17/2026 | |
| 10.10.2603.5 | 760 | 3/16/2026 | |
| 10.10.2603.4 | 726 | 3/14/2026 | |
| 10.10.2603.3 | 657 | 3/8/2026 | |
| 10.10.2603.2 | 633 | 3/2/2026 | |
| 10.10.2603.1 | 643 | 3/2/2026 | |
| 10.10.2602.1 | 872 | 2/6/2026 | |
| 10.10.2601.1 | 323 | 1/1/2026 | |
| 10.10.2512.2 | 685 | 12/10/2025 | |
| 10.10.2512.1 | 402 | 11/27/2025 | |
| 10.10.2511.1 | 510 | 11/12/2025 | |
| 10.9.2511.1 | 417 | 11/4/2025 | |
| 10.9.2510.3 | 381 | 10/25/2025 |
Remove .NET 8 from targets