LHZ.WebSocket 1.1.1

dotnet add package LHZ.WebSocket --version 1.1.1
                    
NuGet\Install-Package LHZ.WebSocket -Version 1.1.1
                    
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="LHZ.WebSocket" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LHZ.WebSocket" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="LHZ.WebSocket" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LHZ.WebSocket --version 1.1.1
                    
#r "nuget: LHZ.WebSocket, 1.1.1"
                    
#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.
#:package LHZ.WebSocket@1.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LHZ.WebSocket&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=LHZ.WebSocket&version=1.1.1
                    
Install as a Cake Tool

LHZ.WebSocket

中文

A lightweight, zero-dependency WebSocket library for .NET, implementing RFC 6455 from the ground up using raw TcpListener / TcpClient. Supports both server-side and client-side WebSocket connections.

Features

  • Zero dependencies — pure .NET, no third-party libraries
  • RFC 6455 compliant — FIN, RSV1–3, all opcodes, masking, extended payload lengths
  • Server & Client — create WebSocket servers or connect as a client via CreateWebSocketClient
  • Fragmented messages — automatic reassembly of continuation frames (client → server)
  • Streaming send — split large payloads across multiple frames via CreateDataFrame(Stream)
  • Bounded channel — producer-consumer pattern for outgoing frames; no unbounded queues
  • Event-drivenOnMessageReceived, OnBytesReceived, OnCloseRecived, OnClientClose, OnPingRecived, OnPongRecived
  • Multi-targetingnet5.0, net6.0, net8.0, net9.0, net10.0 with nullable reference types enabled
  • Handshake timeout — configurable timeout to reject slow HTTP upgrade requests
  • xUnit tested — comprehensive unit tests for frame parsing, close messages, HTTP headers, server, and client

Quick Start

1. Add LHZ.WebSocket Package

Package Manager
Install-Package LHZ.WebSocket -version 1.0.2
.NET CLI
dotnet add package LHZ.WebSocket --Version 1.0.2
Package Reference
<PackageReference Include="LHZ.WebSocket" Version="1.0.2" />

2. Create and start a server

using LHZ.WebSocket;
using LHZ.WebSocket.Core;
using LHZ.WebSocket.Http;
using LHZ.WebSocket.Interfaces;

// Listen on port 5000, all interfaces
var server = new WebSocketServer(5000);

server.OnUpgradeRequest += (HttpContext context) =>
{
    // Optional: inspect headers, add custom response headers, or deny the upgrade
    // context.Response?.Headers.Add("X-Custom", "value");

    var client = context.HttpUpgrade();    // completes the 101 handshake

    client.OnMessageReceived += (IWebSocketClient sender, string message) =>
    {
        Console.WriteLine($"Received: {message}");
        sender.SendMessage($"Echo: {message}");
    };

    client.OnBytesReceived += (IWebSocketClient sender, byte[] data) =>
    {
        Console.WriteLine($"Received {data.Length} bytes");
    };

    client.OnCloseRecived += (IWebSocketClient sender, CloseMessage msg) =>
    {
        Console.WriteLine($"Client closed: {msg.CloseCode} — {msg.Message}");
        sender.Close();
    };
};

server.Start();
Console.WriteLine($"Server started, {server.ClientNums} clients connected");
Console.ReadLine();
server.Stop();

3. Connect as a client

using LHZ.WebSocket;
using LHZ.WebSocket.Core;
using LHZ.WebSocket.Interfaces;

// Connect to a WebSocket server
var client = WebSocketClient.CreateWebSocketClient("ws://localhost:5000/");

client.OnMessageReceived += (IWebSocketClient sender, string message) =>
{
    Console.WriteLine($"Received: {message}");
};

client.OnCloseRecived += (IWebSocketClient sender, CloseMessage message) =>
{
    Console.WriteLine($"Connection closed: {message.CloseCode}");
    sender.Close();
};

client.Open();
client.SendMessage("Hello World!");

4. Bind to a specific IP

var server = new WebSocketServer(IPAddress.Loopback, 5000);

5. Try the browser demo

Open chat-client.html in a browser, or run the unit tests:

cd src
dotnet test

API Reference

WebSocketServer

Member Description
WebSocketServer(int port) Bind to all interfaces on the given port
WebSocketServer(IPAddress ip, int port) Bind to a specific IP and port
WebSocketServer(IPAddress ip, int port, int timeOut) Bind to a specific IP and port with a handshake timeout (seconds)
Start() Begin accepting connections
Stop() Disconnect all clients and stop listening
ClientNums Current number of connected clients
WebSocketClients Snapshot of connected clients (IEnumerable<IWebSocketClient>)
OnUpgradeRequest Fired when an HTTP upgrade is received; call HttpUpgrade() to accept
OnClientConnected Fired after the WebSocket handshake completes (Action<IWebSocketClient>)

IWebSocketClient (interface)

Member Description
ID Unique Guid for this connection
Status Current ClientStatus (Connection / Opend / Close)
SendMessage(string) Send a UTF-8 text frame
SendByte(byte[]) Send a binary frame
Ping(byte[]) Send a Ping frame
Pong(byte[]) Send a Pong frame
Open() Start the background send/receive loops
Close() Cancel tasks and dispose the TCP connection
Dispose() Alias for Close() (implements IDisposable)
OnMessageReceived EventHandler<IWebSocketClient, string> — complete text message
OnBytesReceived EventHandler<IWebSocketClient, byte[]> — complete binary message
OnCloseRecived EventHandler<IWebSocketClient, CloseMessage> — close frame received
OnPingRecived EventHandler<IWebSocketClient, byte[]> — Ping frame received
OnPongRecived EventHandler<IWebSocketClient, byte[]> — Pong frame received
OnClientClose Action<IWebSocketClient> — connection closed (local or remote)

WebSocketClient

Member Description
CreateWebSocketClient(string url, HttpHeaders? headers) Static — creates a client connection to a WebSocket server
HttpContext The underlying HTTP context for this connection
Status Current ClientStatus
SendMessage(string) Send a UTF-8 text frame
SendByte(byte[]) Send a binary frame
Ping(byte[]) Send a Ping frame
Pong(byte[]) Send a Pong frame
Open() Start background send/receive loops
Close() Cancel tasks and dispose the TCP connection
Dispose() Alias for Close()

IHttpContext (interface)

Member Description
Request Parsed HTTP request (method, URL, headers)
Response HTTP response object
Stream The underlying network stream
Status Current HttpContextStatus
HttpUpgrade(int capacity = 1024) Completes the WebSocket handshake and returns the WebSocketClient

HttpContextBase (abstract class)

Base class implementing IHttpContext. Provides HTTP request parsing, timeout handling, and the HttpUpgrade() handshake logic. The concrete HttpContext class adds TcpClient support.

HttpContext (sealed, extends HttpContextBase)

Member Description
Request Parsed HTTP request (method, URL, headers)
Response HTTP response object (nullable; populated for server-side upgrades)
TcpClient The underlying TCP connection
Stream The underlying network stream
Status Current HttpContextStatus (NotInitialized / Initialized / Upgraded / TimedOut / Rejected)
HttpUpgrade(int capacity = 1024) Computes Sec-WebSocket-Accept, writes 101 Switching Protocols, returns the WebSocketClient
WebSocketClient The upgraded client (populated after HttpUpgrade())
Dispose() Disposes the TCP client if no upgrade was performed

HttpRequest

Member Description
Method HTTP method (e.g. GET)
Url Request path (e.g. /chat)
HttpVersion HTTP version string (e.g. HTTP/1.1)
Headers Parsed request headers (System.Net.Http.Headers.HttpHeaders, case-insensitive keys)
WriteToStream(Stream) Writes the request line and headers to a stream (used for client-side handshake)

HttpResponse

Member Description
StatusCode HTTP status code (e.g. HttpStatusCode.SwitchingProtocols)
HttpVersion HTTP version string (e.g. HTTP/1.1)
Headers Response headers (System.Net.Http.Headers.HttpHeaders)

DataFrame

Member Description
CreateDataFrame(OpCode, bool FIN, byte[]? key, byte[] data) Build a single frame from a byte array
CreateDataFrame(OpCode, byte[]? key, Stream data, int maxLen) Split a stream into multiple frames
FIN / RSV1 / RSV2 / RSV3 Frame control flags
Opcode OpCode enum value
Masked Whether the payload is XOR-masked
MaskingKey 4-byte masking key (null if unmasked)
DataFrameHeader Serialized frame header (2–14 bytes)
Data Payload as ArraySegment<byte>

CloseMessage

Member Description
CloseCode RFC 6455 status code (e.g. Normal = 1000)
Message Optional human-readable reason

Delegates

EventHandler<TSender, TEventArgs> — Custom delegate defined in LHZ.WebSocket.Delegates

public delegate void EventHandler<in TSender, TEventArgs>(TSender sender, TEventArgs e);

Enums

OpCodeContinuation (0x0), Text (0x1), Binary (0x2), Close (0x8), Ping (0x9), Pong (0xA)

CloseCode — All RFC 6455 codes: Normal (1000), GoingAway (1001), ProtocolError (1002), …, TlsHandshake (1015)

ClientStatusConnection, Opend, Close

ServerStatusReady, Start, Closing, Closed

HttpContextStatusNotInitialized (0), Initialized (1), Upgraded (2), TimedOut (-2), Rejected (-1)

Architecture

sequenceDiagram
    participant Peer
    participant TcpListener
    participant WebSocketServer
    participant HttpContext
    participant WebSocketClient

    Peer->>TcpListener: TCP connect
    TcpListener->>WebSocketServer: AcceptTcpClientAsync()
    WebSocketServer->>HttpContext: Parse HTTP upgrade request
    WebSocketServer->>+Peer: OnUpgradeRequest (user code calls HttpUpgrade())
    HttpContext->>Peer: HTTP 101 + Sec-WebSocket-Accept
    HttpContext->>WebSocketClient: new WebSocketClient(httpContext)
    WebSocketClient->>WebSocketClient: Open() → StartReceiver() + StartSender()
    Peer->>WebSocketClient: Data frames
    WebSocketClient->>Peer: OnMessageReceived / OnBytesReceived / OnPingRecived / OnPongRecived
    WebSocketClient-->>Peer: SendMessage() / SendByte() / Ping() / Pong()

Internally, each WebSocketClient runs two background tasks:

  • Receiver — reads frames via DataFrameReader, reassembles fragmented messages, dispatches by opcode
  • Sender — dequeues frames from a bounded Channel<DataFrame>, writes header + payload to the network stream

Project Structure

LHZ.WebSocket/
├── src/
│   ├── LHZ.WebSocket/                    # Core library
│   │   ├── WebSocketServer.cs           # TCP listener & client lifecycle
│   │   ├── WebSocketClient.cs           # Per-connection send/receive (server & client)
│   │   ├── Core/
│   │   │   ├── CloseMessage.cs          # Close frame payload
│   │   │   ├── DataFrame.cs             # Frame builder / parser (RFC 6455 §5.2)
│   │   │   └── DataFrameReader.cs       # Frame reader from Stream
│   │   ├── Delegates/
│   │   │   └── EventHandler.cs          # Custom event delegate with `in` modifier
│   │   ├── Enums/
│   │   │   ├── ClientStatus.cs          # Client lifecycle states
│   │   │   ├── CloseCode.cs             # RFC 6455 close status codes
│   │   │   ├── HttpContextStatus.cs     # HTTP context lifecycle states
│   │   │   ├── OpCode.cs                # Frame opcodes
│   │   │   └── ServerStatus.cs          # Server lifecycle states
│   │   ├── Http/
│   │   │   ├── HttpContext.cs           # HTTP upgrade handshake (server & client)
│   │   │   ├── HttpContextBase.cs       # Abstract base with handshake & timeout logic
│   │   │   ├── HttpHeaders.cs           # Internal header collection
│   │   │   ├── HttpRequest.cs           # HTTP request-line & header parser/writer
│   │   │   └── HttpResponse.cs          # HTTP response builder & parser
│   │   └── Interfaces/
│   │       ├── IHttpContext.cs          # HTTP context interface
│   │       └── IWebSocketClient.cs      # WebSocket client interface
│   ├── LHZ.WebSocket.Test/              # xUnit test project
│   │   ├── WebSocketServerTests.cs
│   │   ├── WebSocketClientTests.cs
│   │   └── Core/ / Http/
│   └── LHZ.WebSocket.slnx              # Solution file
├── chat-client.html                     # Browser-based multi-user chat demo
├── test-client.html                     # Browser-based test client
├── LICENSE
├── README.md
└── README.zh-CN.md

Requirements

License

MIT

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  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 is compatible.  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.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net5.0

    • No dependencies.
  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on LHZ.WebSocket:

Package Downloads
LHZ.WebSocket.AspNetCore

A lightweight ASP.NET Core middleware wrapper for the LHZ.WebSocket library, enabling WebSocket upgrade handling and client tracking in ASP.NET Core applications.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 125 8/19/2026
1.0.2 164 8/2/2026
1.0.1 105 7/25/2026
1.0.0 102 7/24/2026

1.1.1:
- Fix: handshake handling is now concurrent, a slow client no longer blocks the accept loop
- Fix: client-role connections now mask every outgoing frame (RFC 6455 §5.1), with a cryptographic masking key
- Fix: the client collection is now per server instance instead of static
- Fix: OnClientClose event subscription no longer leaks
- Fix: masking no longer mutates the caller's buffer
- Fix: client handshake can parse restricted response headers (e.g. Connection)
- Add: server-side handshake validation (Sec-WebSocket-Key / Sec-WebSocket-Version 13) with HTTP 400 rejection
- Add: automatic Close frame reply on receiving a Close frame
- Add: fragmentation validation (reject orphan Continuation frames, control frames are never merged into messages)
- Add: 10 interop tests (client-server round trip, masking, concurrent handshakes, handshake validation, close reply, fragmentation)