Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
W

WatsonTcp

> 开发工具
Open source

WatsonTcp is the easiest way to build TCP-based clients and servers in C#.

672 stars0 likes0 views
WebsiteGitHub

About

WatsonTcp is the easiest way to build TCP-based clients and servers in C#.

WatsonTcp

WatsonTcp is the fastest, easiest, most efficient way to build TCP-based clients and servers in C# with integrated framing, reliable transmission, and fast disconnect detection.

IMPORTANT WatsonTcp provides framing to ensure message-level delivery which also dictates that you must either 1) use WatsonTcp for both the server and the client, or, 2) ensure that your client/server exchange messages with the WatsonTcp node using WatsonTcp's framing. Refer to FRAMING.md for a reference on WatsonTcp message structure.

  • If you want a library that doesn't use framing, but has a similar implementation, use SuperSimpleTcp
  • If you want a library that doesn't use framing and provides explicit control over how much data to read, use CavemanTcp

.NET Foundation

This project is part of the .NET Foundation along with other projects like the .NET Runtime.

Contributions

Special thanks to the following people for their support and contributions to this project!

@brudo @MrMikeJJ @mikkleini @pha3z @crushedice @marek-petak @ozrecsec @developervariety @NormenSchwettmann @karstennilsen @motridox @AdamFrisby @Job79 @Dijkstra-ru @playingoDEERUX @DuAell @syntacs @zsolt777 @broms95 @Antwns @MartyIX @Jyck @Memphizzz @nirajgenius @cee-sharp @jeverz @cbarraco @DenisBalan @Markonius @Ahmed310 @markashleybell @thechosensausage @JVemon @eatyouroats @bendablegears @Laiteux @fisherman6v6 @wesoos @YorVeX @tovich37 @sancheolz @lunedis @ShayanFiroozi @BrvSqr

If you'd like to contribute, please jump right into the source code and create a pull request, or, file an issue with your enhancement request.

New in v6.4.0

Telemetry and Observability

WatsonTcp now emits standardized, vendor-neutral telemetry using only the .NET base class library, so it can be observed by Radiant, the OpenTelemetry SDK, Prometheus, or any compatible host with no dependency on any telemetry backend.

  • metrics are recorded into a System.Diagnostics.Metrics.Meter named WatsonTcp
  • distributed-tracing spans are started from a System.Diagnostics.ActivitySource named WatsonTcp
  • both names are exposed as public constants on the new WatsonTcp.WatsonTcpMetrics class and are stable across releases
  • 24 metrics cover messages, bytes, connections, disconnections (by reason), handshakes, authentication, authorization, synchronous request/response, exceptions, transient listener errors, and uptime
  • metric tags are low-cardinality only (role, protocol, outcome, reason); high-cardinality identifiers (client GUID, remote endpoint, conversation GUID) are placed on spans, never on metric tags
  • telemetry is on by default and is a near-free no-op when nobody subscribes; it can be turned off per instance via Settings.EnableMetrics and Settings.EnableTracing

A telemetry host subscribes by name and nothing else changes in your WatsonTcp code:

// Radiant host
settings.Sources.AddMeter(WatsonTcp.WatsonTcpMetrics.MeterName);            // "WatsonTcp"
settings.Sources.AddActivitySource(WatsonTcp.WatsonTcpMetrics.MeterName);   // "WatsonTcp"

// or a raw OpenTelemetry host
Sdk.CreateMeterProviderBuilder().AddMeter("WatsonTcp").AddPrometheusHttpListener().Build();
Sdk.CreateTracerProviderBuilder().AddSource("WatsonTcp").AddOtlpExporter().Build();

See TELEMETRY.md for the full metric catalog, tag dictionaries, Prometheus series names, and integration walkthrough.

New in v6.3.2

Listener Reliability

WatsonTcp now keeps the server listener running when the underlying TCP accept call hits a transient connection reset or abort before a client is fully established.

Key improvements include:

  • recover from accept-time SocketError.ConnectionReset and SocketError.ConnectionAborted without stopping the accept loop
  • preserve exception reporting through ExceptionEncountered and add warning-level listener logging
  • add regression coverage for accept-loop recovery after a reset

New in v6.3.1

Performance

WatsonTcp now includes a performance-focused maintenance release aimed at lower latency, higher throughput, and faster steady-state operation without changing the public API surface for normal usage.

Key improvements include:

  • buffered header parsing with pushback support instead of allocation-heavy framing reads
  • lower-copy send and receive paths for array-backed payloads and stream-backed message handling
  • reduced hot-path disconnect overhead by relying on real read and write failures instead of proactive liveness polling
  • internal async connection setup and lower-overhead collection and serialization paths

Benchmarking

The repository now includes src/Test.PerformanceBenchmark, a console benchmark suite that measures:

  • response time
  • throughput
  • connection setup time

Run it from the repository root with:

RunBenchmarks.bat

The benchmark writes timestamped summaries to benchmarks/.

New in v6.3.0

Async Stream Receive

WatsonTcp now supports awaited stream callbacks on both server and client:

server.Callbacks.StreamReceivedAsync = async (args, token) =>
{
    await using var file = File.Create("payload.bin");
    await args.DataStream.CopyToAsync(file, 81920, token);
};
client.Callbacks.StreamReceivedAsync = async (args, token) =>
{
    await using var file = File.Create("server-payload.bin");
    await args.DataStream.CopyToAsync(file, 81920, token);
};

Use Callbacks.StreamReceivedAsync when you need to await stream processing, especially for payloads at or above Settings.MaxProxiedStreamSize.

Stream Receive Precedence

Receive mode selection is now:

  • Events.MessageReceived
  • Callbacks.StreamReceivedAsync
  • Events.StreamReceived

Warnings for conflicting receive-mode configuration are emitted through Settings.Logger.

Testing

Shared Touchstone-backed coverage now includes:

  • async stream callback precedence on both client and server
  • small, large, and exact-threshold async stream receive paths
  • partial-read remainder drain validation on both client and server
  • callback exception handling on both client and server
  • sync stream regression checks for partial-read cleanup

New in v6.2.0

Connection Authorization

WatsonTcp now supports an explicit server-side admission callback:

server.Callbacks.AuthorizeConnectionAsync = async (ctx, token) =>
{
    if (!ctx.IpPort.StartsWith("127.0.0.1"))
        return ConnectionAuthorizationResult.Reject("Local connections only.");

    return ConnectionAuthorizationResult.Allow();
};

Rejected connections raise ConnectionRejected events and can surface ConnectionRejectedException on compatible clients.

Custom Handshake State Machines

WatsonTcp also supports framed pre-registration handshakes without exposing the raw stream:

server.Callbacks.HandshakeAsync = async (session, token) =>
{
    HandshakeMessage msg = await session.ReceiveAsync(token);
    string apiKey = Encoding.UTF8.GetString(msg.Data);
    return apiKey == "valid-api-key-123"
        ? HandshakeResult.Succeed()
        : HandshakeResult.Fail("Invalid API key.");
};

client.Callbacks.HandshakeAsync = async (session, token) =>
{
    await session.SendAsync(new HandshakeMessage
    {
        Type = "api-key",
        Data = Encoding.UTF8.GetBytes("valid-api-key-123")
    }, token);

    return HandshakeResult.Succeed();
};

Handshake-enabled servers require compatible clients that understand the new control-plane statuses.

Testing

Automated tests are now defined once in src/Test.Shared and exposed through:

  • src/Test.Automated for the Touchstone CLI runner
  • src/Test.XUnit for dotnet test via xUnit
  • src/Test.NUnit for dotnet test via NUnit

Run them with:

dotnet run --project src/Test.Automated --framework net8.0 -- --results test-results/cli-results.json
dotnet test src/Test.XUnit/Test.XUnit.csproj --framework net8.0
dotnet test src/Test.NUnit/Test.NUnit.csproj --framework net8.0

New in v6.1.0

Performance

  • Rewrote message header parsing to eliminate O(n^2) array allocations and per-byte LINQ overhead; now uses a MemoryStream accumulator with direct byte comparison
  • Send operations now use ArrayPool pooling instead of allocating new buffers on every iteration

Thread Safety

  • Consolidated ClientMetadataManager from 5 independent ReaderWriterLockSlim instances to a single lock, eliminating race conditions during multi-dictionary operations (ReplaceGuid, Remove)
  • Fixed TOCTOU race in GetClient() (ContainsKey then indexer across separate lock acquisitions); now uses TryGetValue
  • Replaced AutoResetEvent + event-based sync response matching with ConcurrentDictionary> in both client and server, eliminating handler registration race conditions and signal loss

Bug Fixes

  • Fixed WaitHandle resource leak in WatsonTcpClient.Connect() (was commented out, now properly closed)
  • Replaced busy-wait spin loops in ClientMetadata.Dispose() and WatsonTcpClient.Disconnect() with Task.Wait(timeout)
  • Stale kicked/timed-out client records now automatically purged every 60 seconds (previously accumulated forever)

New Features

  • Settings.MaxHeaderSize (client and server, default 256KB) guards against memory exhaustion from oversized or malicious headers
  • Settings.EnforceMaxConnections (server, default true) actively rejects connections at capacity; set to false for legacy behavior

Observability

  • Added debug-level logging to all previously silent TaskCanceledException and OperationCanceledException catch blocks

Testing

  • 10 new automated tests (46 total) covering MaxConnections enforcement, MaxHeaderSize validation, rapid connect/disconnect, concurrent sync requests, SSL, server stop detection, duplicate GUIDs, and send-with-offset

Breaking Changes

  • Settings.EnforceMaxConnections defaults to true. If you relied on accepting connections beyond MaxConnections, set EnforceMaxConnections = false.
  • All other changes are internal with identical public API and wire protocol.

Previous in v6.0.x

  • Remove unsupported frameworks
  • Async version of SyncMessageReceived callback
  • Moving usings inside namespace
  • Remove obsolete methods
  • Mark non-async APIs obsolete
  • Modified test projects to use async
  • Ensured background tasks honored cancellation tokens
  • Ability to specify a client's GUID before attempting to connect

Architecture

Refer to ARCHITECTURE.md for a detailed overview of the internal design, message flow, threading model, and key design decisions.

For the wire protocol specification (header format, delimiter, payload layout), see FRAMING.md.

Test Applications

Test projects for both client and server are included which will help you understand and exercise the class library. Shared automated coverage lives in Test.Shared, while Test.Automated, Test.XUnit, and Test.NUnit are the supported unattended test hosts.

SSL

WatsonTcp supports data exchange with or without SSL. The server and client classes include constructors that allow you to include fields for the PFX certificate file and password. An example certificate can be found in the test projects, which has a password of 'password'.

To Stream or Not To Stream...

WatsonTcp allows you to receive messages using either byte arrays or streams.

  • Set Events.MessageReceived if you want a buffered byte[]
  • Set Callbacks.StreamReceivedAsync if you want awaited stream ownership
  • Set Events.StreamReceived if you want the legacy synchrono

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C#apiasync-tcp-serverclientframing

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category开发工具
PricingOpen source

> Related tools

V
VS Code
流行的开源代码编辑器
G
Git
分布式版本控制系统
V
Vite
下一代前端构建工具