A high-performance networking library for .NET 10 designed for real-time multiplayer games. The name "Nexum" comes from Latin, meaning "bond" or "connection" β reflecting the library's purpose of creating reliable network connections. Features TCP and UDP communication with NAT hole punching, reliable UDP, server-orchestrated P2P groups with direct and relayed messaging, AES/RC4 encryption, and zero-allocation optimizations.
Note: This library was inspired by ProudNet, a commercial networking solution.
- TCP (Default) - Reliable, ordered message delivery using DotNetty
- UDP via NAT Hole Punching - Optional low-latency UDP channel established through automatic NAT traversal
- Unreliable UDP - Fire-and-forget for real-time data (position updates, etc.)
- Reliable UDP - Guaranteed delivery over UDP with automatic retransmission and ordering
- MTU Discovery - Binary search algorithm discovers optimal MTU by piggybacking probes on ping/pong packets
- Auto Fragmentation - Large UDP packets automatically fragmented and reassembled with adaptive MTU
- Message Compression - Zlib compression for bandwidth optimization
- Server Time Sync - Client can synchronize with server time
- P2P Groups - Server orchestrates client-to-client UDP connections
- P2P NAT Hole Punching - Direct peer-to-peer connections through NAT
- Direct P2P - Send messages directly between peers over UDP
- P2P MTU Discovery - Each peer pair discovers optimal MTU independently
- Relayed P2P - Fallback relay through server when direct connection fails
- TCP Relay - Relayed messages through server TCP
- UDP Relay - Relayed messages through server UDP (supports both reliable and unreliable)
- RSA Key Exchange - Secure 2048-bit RSA key exchange during connection
- AES Encryption - Secure message encryption (configurable key length, default 256-bit)
- RC4 Fast Encryption - High-performance encryption for real-time data (configurable key length, default 512-bit)
- Per-Session Keys - Unique encryption keys generated for each client session
- Asynchronous I/O - Built on DotNetty for high-performance async networking
- UDP Socket Pool - Multiple UDP listener sockets with random assignment for load distribution
- Efficient Serialization - Custom binary serialization with
NetMessage - Zero-Allocation Patterns -
GC.AllocateUninitializedArray,ArrayPool<T>,stackallocfor small buffers,Span<T>, andBinaryPrimitives - Optimized Thread Pools - DotNetty
MultithreadEventLoopGroupauto-scales to CPU core count - Adaptive Fragmentation - MTU inferred from incoming fragments for efficient reassembly
- Compile-Time Code Generation - Roslyn source generator automatically creates serialization and deserialization methods
- Attribute-Based - Simple attributes define packet structure and property order
- Type-Safe - Strong typing for packet properties with automatic serialization order
- Custom Serializers - Support for custom serializers for complex or non-standard types
- Zero Boilerplate - No manual serialization code needed for marked packets
Nexum/
βββ BaseLib/ # Core utilities and extensions
β βββ Caching/
β β βββ MemoryCache.cs
β βββ Extensions/
β β βββ ByteArrayExtensions.cs
β β βββ ConcurrentDictionaryExtensions.cs
β β βββ DateTimeExtensions.cs
β β βββ DictionaryExtensions.cs
β β βββ ExceptionExtensions.cs
β β βββ IPEndPointExtensions.cs
β β βββ SemaphoreSlimExtensions.cs
β β βββ StreamExtensions.cs
β β βββ SymmetricAlgorithmExtensions.cs
β βββ Hashing/
β β βββ CRC32.cs
β β βββ Hash.cs
β βββ IO/
β β βββ NonClosingStream.cs
β βββ Logging/
β β βββ ContextEnricher.cs
β βββ Patterns/
β β βββ Singleton.cs
β βββ Threading/
β β βββ TaskLoop.cs
β β βββ ThreadLoop.cs
β βββ Events.cs
βββ Nexum.SourceGen/ # Roslyn source generator for packet serialization
β βββ NetSerializableGenerator.cs
βββ Nexum.Core/ # Shared networking core
β βββ Logging/
β β βββ BurstDuplicateLogFilter.cs
β β βββ BurstDuplicateLogger.cs
β βββ Nexum/
β βββ Attributes/ # Source generator attributes
β β βββ INetPropertySerializer.cs
β β βββ NetCoreMessageAttribute.cs
β β βββ NetPropertyAttribute.cs
β β βββ NetSerializableAttribute.cs
β β βββ ScalarSerializer.cs
β β βββ StringEndPointSerializer.cs
β β βββ UnicodeStringSerializer.cs
β βββ Configuration/ # Settings and configuration
β β βββ Constants.cs
β β βββ Enums.cs
β β βββ FragmentConfig.cs
β β βββ HolepunchConfig.cs
β β βββ MtuConfig.cs
β β βββ NetConfig.cs
β β βββ NetSettings.cs
β β βββ ReliableUdpConfig.cs
β βββ Crypto/ # Encryption and compression
β β βββ NetCrypt.cs
β β βββ NetZip.cs
β β βββ RSAHelper.cs
β βββ DotNetty/Codecs/ # DotNetty codec implementations
β β βββ LengthFieldBasedFrameDecoder.cs
β β βββ NexumFrameDecoder.cs
β β βββ NexumFrameEncoder.cs
β β βββ ReliableUdpCodecHandler.cs
β β βββ UdpDefragmentationDecoder.cs
β β βββ UdpFrameDecoder.cs
β β βββ UdpFragmentationEncoder.cs
β β βββ UdpFrameEncoder.cs
β βββ Events/ # Event arguments
β β βββ ConnectionStateChangedEventArgs.cs
β β βββ SessionConnectionStateChangedEventArgs.cs
β βββ Fragmentation/ # UDP packet fragmentation types
β β βββ AssembledPacket.cs
β β βββ AssembledPacketError.cs
β β βββ DefraggingPacket.cs
β βββ Holepunching/ # NAT hole punching
β β βββ HolepunchHelper.cs
β βββ Message/ # Core message packets
β βββ Mtu/ # MTU discovery
β β βββ MtuDiscovery.cs
β βββ ReliableUdp/ # Reliable UDP implementation
β β βββ CompressedFrameNumbers.cs
β β βββ ReliableUdpFrame.cs
β β βββ ReliableUdpHelper.cs
β β βββ ReliableUdpHost.cs
β β βββ ReliableUdpReceiver.cs
β β βββ ReliableUdpSender.cs
β β βββ StreamQueue.cs
β βββ Rmi/ # RMI packets (S2C, C2S, C2C)
β βββ Routing/ # Host identification
β β βββ FilterTag.cs
β β βββ HostId.cs
β βββ Serialization/ # Binary serialization
β β βββ ByteArray.cs
β β βββ NetMessage.cs
β βββ Simulation/ # Network simulation for testing
β β βββ NetworkProfile.cs
β β βββ NetworkSimulation.cs
β β βββ SimulatedUdpChannelHandler.cs
β βββ Udp/ # UDP message types
β β βββ OutboundUdpPacket.cs
β β βββ ReliableUdpMessages.cs
β β βββ UdpMessage.cs
β βββ Utilities/ # Helper utilities
β β βββ EventLoopScheduler.cs
β β βββ Extensions.cs
β β βββ NetUtil.cs
β β βββ SysUtil.cs
β βββ ITimeSource.cs
β βββ ModuleInit.cs
β βββ NetCore.cs
β βββ NetCoreHandler.cs
βββ Nexum.Client/ # Client-side implementation
β βββ Nexum/
β βββ Core/ # Client core
β β βββ NetClient.cs
β β βββ NetClientAdapter.cs
β β βββ NetClientHandler.cs
β βββ P2P/ # P2P client components
β β βββ P2PGroup.cs
β β βββ P2PMember.cs
β βββ Udp/ # UDP handling
β β βββ RecycledUdpSocket.cs
β β βββ UdpHandler.cs
β βββ Utilities/ # Client-specific utilities
β βββ NetUtil.cs
β βββ SysUtil.cs
βββ Nexum.Server/ # Server-side implementation
β βββ Nexum/
β βββ Core/ # Server core
β β βββ ChannelAttributes.cs
β β βββ HostIdFactory.cs
β β βββ NetServer.cs
β β βββ NetServerAdapter.cs
β β βββ NetServerHandler.cs
β βββ P2P/ # P2P server components
β β βββ P2PConnectionState.cs
β β βββ P2PGroup.cs
β β βββ P2PMember.cs
β βββ Sessions/ # Session management
β β βββ NetSession.cs
β β βββ SessionHandler.cs
β βββ Udp/ # UDP handling
β βββ UdpHandler.cs
β βββ UdpSocket.cs
βββ Nexum.Tests/ # Unit and integration tests
β βββ Integration/
β β βββ ConnectionStateTests.cs
β β βββ ConnectionTests.cs
β β βββ EdgeCaseTests.cs
β β βββ IntegrationTestBase.cs
β β βββ IntegrationTestCollection.cs
β β βββ KeyExchangeTests.cs
β β βββ MtuDiscoveryTests.cs
β β βββ P2PConnectionTests.cs
β β βββ ReliableUdpTests.cs
β β βββ StressTests.cs
β β βββ UdpConnectionTests.cs
β β βββ UdpFragmentationTests.cs
β β βββ UdpReconnectionTests.cs
β βββ ByteArrayTests.cs
β βββ CRC32Tests.cs
β βββ NetCryptTests.cs
β βββ NetMessageTests.cs
β βββ NetPacketSourceGenTests.cs
β βββ NetZipTests.cs
βββ Nexum.Tests.E2E/ # End-to-end AWS tests
β βββ Orchestration/
β β βββ Ec2Orchestrator.cs
β β βββ IamProvisioner.cs
β β βββ S3Deployer.cs
β β βββ SsmCommandRunner.cs
β βββ AwsConfig.cs
β βββ CoreFeaturesE2ETest.cs
βββ Nexum.E2E.Client/ # E2E test client application
β βββ Program.cs
βββ Nexum.E2E.Server/ # E2E test server application
β βββ Program.cs
βββ Nexum.E2E.Common/ # Shared E2E constants
β βββ E2EConstants.cs
βββ Example.Client/ # Example client application
β βββ Program.cs
βββ Example.Server/ # Example server application
βββ Program.cs
- .NET 10.0 SDK or later
- Visual Studio 2022+ or VS Code with C# extension
Clone the repository and build the solution:
git clone https://github.com/aizuon/nexum.git
cd nexum
dotnet build Nexum.slnusing System.Net;
using Nexum.Core;
using Nexum.Server;
const string serverName = "Relay";
var serverGuid = new Guid("a43a97d1-9ec7-495e-ad5f-8fe45fde1151");
// Create a server instance
var server = new NetServer(serverName, serverGuid);
// Handle incoming RMI messages
server.OnRmiReceive += (session, message, rmiId) =>
{
switch (rmiId)
{
case 1: // Custom message handler
// Read message data
message.Read(out int value);
// Send response back to client
var response = new NetMessage();
response.Write(value * 2);
session.RmiToClient(2, response);
break;
}
};
// Start listening with TCP and UDP ports
await server.ListenAsync(
new IPEndPoint(IPAddress.Any, 28000), // TCP endpoint
new uint[] { 29000, 29001, 29002, 29003 } // UDP ports
);using System.Net;
using Nexum.Core;
using Nexum.Client;
const string serverName = "Relay";
var serverGuid = new Guid("a43a97d1-9ec7-495e-ad5f-8fe45fde1151");
// Create a client instance
var client = new NetClient(serverName, serverGuid);
// Handle connection completion
client.OnConnectionComplete += () =>
{
Console.WriteLine($"Connected with HostId: {client.HostId}");
// Send a message to the server
var message = new NetMessage();
message.Write(42);
client.RmiToServer(1, message);
};
// Handle incoming RMI messages
client.OnRmiReceive += (message, rmiId) =>
{
message.Read(out int result);
Console.WriteLine($"Received response: {result}");
};
// Connect to server
await client.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 28000));Configure the server behavior using NetSettings:
var settings = new NetSettings
{
// Transport settings
EnableNagleAlgorithm = true, // TCP Nagle algorithm
IdleTimeout = 900, // Session idle timeout (seconds)
// Message settings
MessageMaxLength = 1048576, // Max message size (1MB)
// Security settings
EncryptedMessageKeyLength = 256, // AES key length (bits)
FastEncryptedMessageKeyLength = 512, // RC4 key length (bits)
// P2P settings
EnableP2PEncryptedMessaging = false, // Encryption for P2P messages
DirectP2PStartCondition = DirectP2PStartCondition.Always, // When to initiate direct P2P holepunching
};
var server = new NetServer("Relay", new Guid("a43a97d1-9ec7-495e-ad5f-8fe45fde1151"), settings);// Server-side: Create a P2P group and add clients
var group = server.CreateP2PGroup();
// Add clients to the group
group.Join(session1);
group.Join(session2);
// Remove clients from the group
group.Leave(session1);// After joining a P2P group, access peers
var peer = client.P2PGroup.P2PMembers[targetHostId];
// Send message to peer (via relay if direct connection not established)
var message = new NetMessage();
message.Write("Hello, peer!");
peer.RmiToPeer(7001, message, reliable: true, relay: true);
// Send directly (requires established direct connection)
peer.RmiToPeer(7001, message, reliable: false, relay: false);NetMessage provides comprehensive serialization support:
var message = new NetMessage();
// Write primitive types
message.Write(42); // int
message.Write(3.14f); // float
message.Write(true); // bool
message.Write("Hello"); // string (Latin1)
message.Write("Hello", unicode: true);// string (Unicode)
// Write complex types
message.Write(Guid.NewGuid()); // Guid
message.Write(new Version(1, 2, 3, 4)); // Version
message.Write(new IPEndPoint(IPAddress.Loopback, 8080)); // IPEndPoint
message.Write(new ByteArray(data)); // ByteArray
message.Write(MyEnum.Value); // Enums
// Read data
message.Read(out int value);
message.Read(out string text);
message.Read(out Guid guid);
message.Read(out MyEnum enumValue);Use [NetSerializable] to define DTOs with automatic serialization. The source generator creates Serialize() and Deserialize() methods at compile time:
using Nexum.Core.Attributes;
[NetSerializable]
public partial class PositionDto
{
[NetProperty(0)]
public float X { get; set; }
[NetProperty(1)]
public float Y { get; set; }
[NetProperty(2)]
public float Z { get; set; }
}
// Serialize
var dto = new PositionDto { X = 10.5f, Y = 0f, Z = -5.2f };
var message = dto.Serialize();
// Deserialize
if (PositionDto.Deserialize(message, out var received))
{
Console.WriteLine($"Position at ({received.X}, {received.Y}, {received.Z})");
}Use [NetRmi] to define RMI packets with automatic ID assignment. The generated Serialize() method wraps the packet in an RmiMessage with the specified RMI ID:
// Define an enum for your RMI IDs (must use ushort as underlying type)
public enum GameRmiId : ushort
{
PlayerMove = 1001,
PlayerAttack = 1002,
ChatMessage = 1003
}
[NetRmi(GameRmiId.PlayerMove)]
public partial class PlayerMoveRmi
{
[NetProperty(0)]
public uint PlayerId { get; set; }
[NetProperty(1)]
public PositionDto Position { get; set; }
}
// Server-side: Send RMI to client
var rmi = new PlayerMoveRmi
{
PlayerId = 1,
Position = new PositionDto { X = 10.5f, Y = 20.3f, Z = 0f }
};
session.RmiToClient(rmi); // TCP
session.RmiToClientUdpIfAvailable(rmi); // UDP if available
// Client-side: Handle incoming RMI
client.OnRmiReceive += (message, rmiId) =>
{
if (rmiId == (ushort)GameRmiId.PlayerMove &&
PlayerMoveRmi.Deserialize(message, out var move))
{
Console.WriteLine($"Player {move.PlayerId} moved to ({move.Position.X}, {move.Position.Y}, {move.Position.Z})");
}
};You can also use raw ushort values for RMI IDs:
[NetRmi(1001)]
public partial class PlayerMoveRmi { /* ... */ }Custom serializers can be specified for complex types:
[NetSerializable]
public partial class ServerInfo
{
[NetProperty(0, typeof(StringEndPointSerializer))]
public IPEndPoint Endpoint { get; set; }
[NetProperty(1, typeof(UnicodeStringSerializer))]
public string ServerName { get; set; }
}Implement custom serializers by implementing INetPropertySerializer<T>:
public sealed class UnixTimestampSerializer : INetPropertySerializer<DateTime>
{
public static void Serialize(NetMessage msg, DateTime obj)
{
long unixTime = new DateTimeOffset(obj).ToUnixTimeMilliseconds();
msg.Write(unixTime);
}
public static bool Deserialize(NetMessage msg, out DateTime obj)
{
if (!msg.Read(out long unixTime))
{
obj = default;
return false;
}
obj = DateTimeOffset.FromUnixTimeMilliseconds(unixTime).UtcDateTime;
return true;
}
}- Client connects via TCP
- Server sends RSA public key (2048-bit)
- Client generates AES and RC4 session keys
- Client encrypts keys with server's RSA public key
- Encrypted keys sent to server
- Server decrypts and stores session keys
- All subsequent communication uses session keys
| Mode | Algorithm | Use Case |
|---|---|---|
Secure |
AES | Sensitive data, authentication |
Fast |
RC4 | Real-time game data, position updates |
None |
- | Non-sensitive data |
Run the test suite:
# Run unit tests
dotnet test Nexum.Tests/Nexum.Tests.csproj --filter "FullyQualifiedName!~Integration"
# Run integration tests
dotnet test Nexum.Tests/Nexum.Tests.csproj --filter "FullyQualifiedName~Integration"
# Run all tests with coverage
dotnet test Nexum.Tests/Nexum.Tests.csproj --collect:"XPlat Code Coverage"βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NetServer β
β βββββββββββββββ βββββββββββββββ βββββββββββββββββββββββββββ β
β β TCP Channel β β UDP Sockets β β P2P Groups β β
β β (DotNetty) β β (Pool) β β βββββββ βββββββ β β
β ββββββββ¬βββββββ ββββββββ¬βββββββ β βGrp 1β βGrp 2β ... β β
β β β β ββββ¬βββ ββββ¬βββ β β
β ββββββββββ¬ββββββββ βββββββΌβββββββββΌβββββββββββ β
β β β β β
β ββββββββββΌβββββββββ β β β
β β NetSession ββββββββββββββββ β β
β β βββββββββββββ β β β
β β β NetCrypt β β β β
β β βββββββββββββ β β β
β ββββββββββ¬βββββββββ β β
ββββββββββββββββββββΌβββββββββββββββββββββββββββββββββΌββββββββββββββ
β β
βββββββββββΌββββββββββ ββββββββββΌβββββββββ
β NetClient β β NetClient β
β βββββββββββββββ ββββββββββββββΊβ (P2P Direct or β
β β P2PGroup β β Direct β Relay via β
β β P2PMember β β P2P β Server) β
β βββββββββββββββ β βββββββββββββββββββ
βββββββββββββββββββββ
| Package | Version | Purpose |
|---|---|---|
| DotNetty.Transport | 0.7.6 | Async networking framework |
| DotNetty.Codecs | 0.7.6 | Frame encoding/decoding |
| BouncyCastle.Cryptography | 2.6.2 | AES/RC4 encryption |
| Serilog | 4.3.0 | Structured logging |
Clients and servers identify the target server using:
ServerName(string): used for logging/contextServerGuid(Guid): used to validate the handshake target
The client sends ServerGuid during the connection handshake, and the server validates it before accepting the connection.
The following features are planned or partially implemented:
- Advanced UDP Congestion Control - Enhance
ReliableUdpHandlerwith TCP-friendly rate control (TFRC) or BBR-style algorithms to prevent packet loss under load - Super Peer / Host Selection - Automatically elect the best peer (lowest latency, best connectivity) as host in P2P groups for authoritative state sync
- WiFi/Network Handover - Seamless reconnection when the client's network changes (e.g., WiFiβmobile), preserving session state and recovering in-flight messages
| Setting | Type | Default | Description |
|---|---|---|---|
NetSettings.EnableNagleAlgorithm |
bool |
true |
TCP Nagle algorithm |
NetSettings.IdleTimeout |
double |
900 | Session idle timeout in seconds |
NetSettings.MessageMaxLength |
uint |
1048576 | Maximum message size |
NetSettings.EncryptedMessageKeyLength |
uint |
256 | AES key length in bits |
NetSettings.FastEncryptedMessageKeyLength |
uint |
512 | RC4 key length in bits |
NetSettings.EnableP2PEncryptedMessaging |
bool |
false |
Encryption for P2P messages |
NetSettings.DirectP2PStartCondition |
DirectP2PStartCondition |
Always |
When to initiate direct P2P holepunching |
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- DotNetty - High-performance networking framework
- BouncyCastle - Cryptography library
- Serilog - Structured logging