SetNet 1.0.0
See the version list below for details.
dotnet add package SetNet --version 1.0.0
NuGet\Install-Package SetNet -Version 1.0.0
<PackageReference Include="SetNet" Version="1.0.0" />
<PackageVersion Include="SetNet" Version="1.0.0" />
<PackageReference Include="SetNet" />
paket add SetNet --version 1.0.0
#r "nuget: SetNet, 1.0.0"
#:package SetNet@1.0.0
#addin nuget:?package=SetNet&version=1.0.0
#tool nuget:?package=SetNet&version=1.0.0
SetNet π
A lightweight, high-throughput .NET networking library for clientβserver games and real-time apps β over TCP, UDP, or both at once.
SetNet gives you a persistent, message-oriented connection with automatic handler registration, a pluggable transport (reliable TCP, raw/reliable UDP, or both together), per-message delivery selection, and production-grade hardening β so you can focus on your game/app logic instead of sockets.
// per-message channel selection β reliable for events, unreliable for movement
await SendAsync(MsgType.Chat, chat, DeliveryMethod.Reliable);
await SendAsync(MsgType.Position, position, DeliveryMethod.Unreliable);
Why SetNet
- π¦ TCP / UDP / Both β one API, choose per
Configuration.TransportType; pick the channel per message viaDeliveryMethod. - π‘οΈ Reliable UDP, optional β sequence / ACK / retransmit / ordered delivery with a bounded receive window and back-pressure; multiple independent channels (
UdpReliableChannels) so a loss on one stream never head-of-line-blocks another. - π€ Emulated UDP connections β handshake + heartbeat give UDP the same
OnConnected/OnDisconnected/peer lifecycle as TCP. Both mode binds a TCP lifeline and a UDP channel to one logical peer, with graceful TCP-only fallback. - π Lifecycle done right β intentional vs unexpected disconnects, auto-reconnect hooks, heartbeat liveness;
OnDisconnectedfires exactly once. - β‘ Fast β ~1.6M msgs/sec on one connection with send batching, ~10 KB per endpoint; allocation-light hot paths.
- π Production-hardened β TLS over TCP, connection/UDP-peer caps, per-IP rate limiting, frame-size cap, back-pressure, bounded inbound queues (OOM protection), a resilient accept loop, and live
NetworkMetrics. - π§© Auto handler registration β mark a class
[MessageHandler(type)]; reflection wires it up. Handlers are strongly typed βIServerMessageHandler<T>/IClientMessageHandler<T>receive the deserialized message; the library (de)serializes for you. - π Raw relay escape hatch β override
OnRawFrame(type, data)to intercept frames andSendRawAsyncto forward bytes without (de)serializing β build an Among Us-style relay/proxy with zero overhead, while normal handlers stay typed. - π¦ Pluggable serialization β the core bundles no serializer. Pick a format via
ISerializer: drop in theSetNet.MessagePackpackage (hardened MessagePack), or supply your own JSON/Protobuf/custom adapter, and register it once withSetNetSerializer.Use(...).
Install
Requires .NET Standard 2.1 (consumable from .NET Core 3.0+/.NET 5β8, Unity, Mono, MAUI β not .NET Framework).
dotnet add package SetNet
# the core bundles no serializer β add one (or supply your own ISerializer):
dotnet add package SetNet.MessagePack
Then register the serializer once at startup, before connecting:
SetNetSerializer.Use(new MessagePackNetSerializer()); // from SetNet.MessagePack
Unity: works on desktop/mobile standalone (Unity 2021+, netstandard2.1). Two things to know: message handlers run on background threads, so marshal to the main thread before touching the Unity API (e.g. queue and drain in
Update()); and on IL2CPP/AOT builds, MessagePack needs pre-generated formatters (or swap in an AOT-friendly serializer β see Serialization). WebGL is not supported (no threads/sockets).
Quick start
1. Define messages (MessagePack DTOs):
public enum MsgType : ushort { Chat = 1 }
[MessagePackObject]
public class ChatMessage { [Key(0)] public string Text { get; set; } = ""; }
2. Server:
using SetNet.Core;
using SetNet.Config;
public class ChatPeer : BasePeer
{
public ChatPeer(PeerInfo info) : base(info) { }
protected override void OnDisconnected() { }
protected override void OnError(string error) { }
}
public class ChatServer : BaseServer
{
public ChatServer(Configuration config) : base(config) { }
protected override BasePeer OnNewClient(PeerInfo info) => new ChatPeer(info);
}
await new ChatServer(new Configuration { Host = "0.0.0.0", Port = 5000 }).StartAsync();
3. Client:
public class ChatClient : BaseClient
{
public ChatClient(Configuration config) : base(config) { }
protected override void OnConnected() => Console.WriteLine("connected");
protected override void OnDisconnected() { }
protected override void OnError(string error) { }
public Task SayAsync(string text) => SendAsync((ushort)MsgType.Chat, new ChatMessage { Text = text });
}
var client = new ChatClient(new Configuration { Host = "127.0.0.1", Port = 5000 });
await client.ConnectAsync();
await client.SayAsync("hello");
4. Handle messages (auto-discovered, strongly typed β the library deserializes for you):
[MessageHandler((ushort)MsgType.Chat)]
public class ChatHandler : IServerMessageHandler<ChatMessage>
{
public Task HandleAsync(BasePeer peer, ChatMessage msg)
{
Console.WriteLine(msg.Text);
return Task.CompletedTask;
}
}
A full runnable chat (separate server + client processes) is in examples/.
Serialization
The core library bundles no serializer β you choose the format behind the ISerializer seam and register it once at startup.
MessagePack (recommended) via the SetNet.MessagePack package β MessagePackNetSerializer is hardened with the UntrustedData security profile (deserialization-DoS protection):
using SetNet.MessagePack;
SetNetSerializer.Use(new MessagePackNetSerializer()); // once, at startup
Or your own format (JSON, Protobuf, MemoryPack, β¦) β implement ISerializer:
public sealed class JsonSerializer : ISerializer
{
public byte[] Serialize<T>(T value) => System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(value);
public T Deserialize<T>(byte[] data) => System.Text.Json.JsonSerializer.Deserialize<T>(data)!;
}
SetNetSerializer.Use(new JsonSerializer()); // once, at startup
Handlers are strongly typed β they receive the deserialized message directly (IServerMessageHandler<ChatMessage> β HandleAsync(peer, ChatMessage msg)); the library serializes on send and deserializes on receive through this one registered serializer. Both ends of a connection must use the same serializer. (Until one is registered, send/receive throws a clear "configure a serializer" error.)
Transport selection
Set Configuration.TransportType (default Tcp, so existing TCP code is unchanged):
| TransportType | DeliveryMethod | Carried over |
|---|---|---|
Tcp |
any | TCP |
Udp |
Reliable | UDP reliability layer (needs UdpReliabilityEnabled) |
Udp |
Unreliable | raw UDP datagram |
Both |
Reliable | TCP |
Both |
Unreliable | UDP (falls back to TCP until the UDP channel attaches) |
var config = new Configuration
{
Host = "127.0.0.1", Port = 5000,
TransportType = TransportType.Both,
UdpReliabilityEnabled = true,
UdpReliableChannels = 2, // independent ordered streams
DefaultDelivery = DeliveryMethod.Reliable,
};
Lifecycle at a glance
BaseClient distinguishes intentional from unexpected disconnects; OnDisconnected fires exactly once.
| Event | OnError | OnUnexpectedDisconnect | OnDisconnected | Auto-Reconnect |
|---|---|---|---|---|
Disconnect() (intentional) |
β | β | β | β |
| Network error / server crash | β | β | β (if reconnect fails) | β (if enabled) |
| Graceful server close | β | β | β | β |
Enable: AutoReconnect = true, HeartbeatEnabled = true (both off by default).
Production hardening
var config = new Configuration
{
Host = "0.0.0.0", Port = 5000,
UseSsl = true, ServerCertificate = cert, // TLS over TCP (UDP is not encrypted)
MaxConnectionsLimit = 5000,
MaxConnectionsPerIpPerSecond = 20, // per-IP rate limit
MaxInFlightMessages = 256, // handler back-pressure
MaxInboundQueue = 16384, // per-connection inbound cap (OOM protection)
HeartbeatEnabled = true,
};
Authentication is intentionally left to your application β validate inside OnNewClient/handlers. UDP has no per-packet encryption; route sensitive data over TLS-over-TCP (or Both with reliable delivery).
Performance
In-process benchmark (dotnet run -c Release --project SetNet.Tests -- bench, ServerGC):
| Mode | Throughput (1 connection) | Optimized for |
|---|---|---|
Batched (SendBatching = true) |
~1.6M msgs/sec | throughput |
Default (TcpNoDelay = true) |
~230k msgs/sec | latency |
~10 KB per endpoint; 2,000 connections established in ~110 ms. The default favors latency (every small message sent immediately); enable SendBatching for high message rates. These numbers include serialization cost β the library deserializes each inbound message into the handler's typed T. Full model, scaling limits and roadmap: docs/PERFORMANCE.en.md.
Documentation
- π User guide (docs/GUIDE.en.md) β full usage manual: handlers, transports, reliable channels, reconnect, batching, hardening, the complete
Configurationreference, and a production checklist. (Π£ΠΊΡΠ°ΡΠ½ΡΡΠΊΠΎΡ: docs/GUIDE.ua.md) - βοΈ Performance (docs/PERFORMANCE.en.md) β performance model, scaling limits, structural roadmap. (Π£ΠΊΡΠ°ΡΠ½ΡΡΠΊΠΎΡ: docs/PERFORMANCE.ua.md)
- ποΈ CLAUDE.md / AGENTS.md β architecture overview for contributors and coding agents.
Build & test
dotnet build # build (library targets netstandard2.1)
dotnet test SetNet.UnitTests/SetNet.UnitTests.csproj # 78 unit + integration tests
dotnet run --project SetNet.Tests -- <frag|tcp|udp|loss|both|idle|deadlock> # in-process transport scenarios
dotnet run --project SetNet.Tests -- bench # throughput / connection benchmark
# chat example (two terminals)
dotnet run --project examples/Chat.Server -- 127.0.0.1 5000
dotnet run --project examples/Chat.Client -- 127.0.0.1 5000 alice
Project structure
SetNet/ core library (transport abstraction, reliability, hardening) β no serializer dependency
SetNet.MessagePack/ MessagePack ISerializer adapter (companion package)
SetNet.Tests/ in-process scenario harness + benchmark
SetNet.UnitTests/ xUnit unit + integration tests
examples/ runnable chat (Chat.Shared / Chat.Server / Chat.Client)
docs/ GUIDE.{en,ua}.md, PERFORMANCE.{en,ua}.md
Status
SetNet has been through extensive adversarial auditing (multi-round correctness convergence + a performance pass) with a full unit/integration suite and in-process scenarios. It is well-suited as the network layer for .NET β .NET real-time systems (multiplayer games, chat, collaborative apps).
It is not a general-purpose RPC/HTTP framework: there is no request/response correlation, no DI/hosting integration, and no WebSocket/browser transport. Before production, implement authentication, set the hardening config, and run a soak/load test under realistic traffic.
License
MIT β see LICENSE.
| Product | Versions 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. net9.0 was computed. 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 was computed. 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. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- No dependencies.
NuGet packages (76)
Showing the top 5 NuGet packages that depend on SetNet:
| Package | Downloads |
|---|---|
|
SetNet.StateSync
Server-authoritative entity replication for SetNet: fixed-rate, delta-compressed world snapshots over the unreliable channel with reliable spawns/despawns, client-side interpolation, interest management, and an input channel for client prediction. Engine-agnostic core (headless dedicated server + any .NET client); the Unity binding (SetNet.StateSync.Unity) adds NetworkObject/NetworkTransform/NetworkAnimator/NetworkRigidbody. Composition, no base class. Depends only on SetNet. |
|
|
SetNet.MessagePack
MessagePack serializer for SetNet. Provides MessagePackNetSerializer (an ISerializer) hardened with the MessagePack UntrustedData security profile. Register it once at startup: SetNetSerializer.Use(new MessagePackNetSerializer()); |
|
|
SetNet.Rooms
Rooms / lobbies for SetNet, by composition (no base class). Create and join rooms by code, broadcast within a room, and get player-joined/left events β on a dedicated server (server is the hub; no relay needed). Pluggable room store (default in-memory), auto-leave on disconnect. Serializer-agnostic; depends only on SetNet. |
|
|
SetNet.Inventory
Server-authoritative player inventory for SetNet: game logic grants/revokes stackable items by player key (server.UseInventory()), connected clients read + subscribe to changes (client.UseInventory()). Atomic TryRevoke primitive backs trades and mail claims; pluggable IInventoryStore (memory default, swap for Redis/DB). Depends only on SetNet. |
|
|
SetNet.Auth
Authentication and sessions for SetNet, by composition (no base class). Enforced gate: until a peer authenticates, its application frames (regular messages and RPC) are dropped; you validate a token via IAuthenticator. Includes session store with TTL, multi-session policy, and automatic reconnect-resume. Serializer-agnostic; depends only on SetNet. Use over TLS. |
GitHub repositories
This package is not used by any popular GitHub repositories.