Production Server Example¶
Learning Signals
- Level: Advanced
- Time: 20–30 minutes
- Prerequisites: Quickstart, Architecture
This guide describes how to build a production-ready Nalix application using best practices for logging, middleware, error handling, and structured contracts.
1. Project Setup¶
A production Nalix project typically splits into at least three assemblies:
- Contracts: Shared POCOs with serialization attributes.
- Server: The host application.
- Client: The integration SDK or test suite.
dotnet new classlib -n MyNet.Contracts
dotnet new console -n MyNet.Server
dotnet new console -n MyNet.Client
2. Shared Contracts (MyNet.Contracts)¶
Use [SerializePackable] to define your packets and keep the contract shared between server and client.
using Nalix.Abstractions.Networking.Packets;
using Nalix.Abstractions.Serialization;
namespace MyNet.Contracts;
[SerializePackable(SerializeLayout.Explicit)]
public sealed class DataRequest : PacketBase<DataRequest>
{
public const ushort OpCodeValue = 0x2001;
[SerializeOrder(0)]
public long RequestId { get; set; }
[SerializeDynamicSize(128)]
[SerializeOrder(1)]
public string Payload { get; set; } = string.Empty;
public DataRequest() => OpCode = OpCodeValue;
}
[SerializePackable(SerializeLayout.Explicit)]
public sealed class DataResponse : PacketBase<DataResponse>
{
public const ushort OpCodeValue = 0x2002;
[SerializeOrder(0)]
public long RequestId { get; set; }
[SerializeDynamicSize(128)]
[SerializeOrder(1)]
public string Message { get; set; } = string.Empty;
public DataResponse() => OpCode = OpCodeValue;
}
3. Server Implementation (MyNet.Server)¶
In production, use NetworkApplication.CreateBuilder() to orchestrate services and protection policies.
The builder auto-registers the built-in handshake, session, and system-control handlers, so your guide only needs to add the domain-specific packets and controllers.
Handler¶
[PacketController("DataHandlers")]
public sealed class DataHandlers
{
private readonly ILogger _logger;
public DataHandlers(ILogger logger) => _logger = logger;
[PacketOpcode(DataRequest.OpCodeValue)]
public DataResponse OnRequest(IPacketContext<DataRequest> context)
{
_logger.LogInformation("Received request {Id}: {Payload}",
context.Packet.RequestId, context.Packet.Payload);
return new DataResponse
{
RequestId = context.Packet.RequestId,
Message = "Success"
};
}
}
Server Host¶
using Nalix.Abstractions.Networking;
using Nalix.Hosting;
using Nalix.Logging;
using Nalix.Network.Protocols;
using Nalix.Network.Options;
using Nalix.Runtime.Middleware.Standard;
using Nalix.Runtime.Dispatching;
var logger = NLogix.Host.Instance;
using var app = NetworkApplication.CreateBuilder()
.ConfigureLogging(logger)
.Configure<NetworkSocketOptions>(opt => {
opt.Port = 5000;
opt.Backlog = 1024;
})
// 1. Register contracts assembly for auto-discovery
.ScanPackets<DataRequest>()
// 2. Add Handlers (resolved via InstanceManager)
.ScanHandlers<DataHandlers>()
// 3. Configure Dispatch Middleware
.ConfigureDispatch(dispatch => {
dispatch.WithLogging(logger)
.WithMiddleware(new ConcurrencyMiddleware())
.WithErrorHandling((ex, cmd) => logger.Error("Unhandled!", ex));
})
// 4. Bind Transport
.BindTcp<ProductionProtocol>().Bind()
.Build();
await app.RunAsync();
public sealed class ProductionProtocol : Protocol
{
private readonly IPacketDispatch _dispatch;
public ProductionProtocol(IPacketDispatch dispatch) => _dispatch = dispatch;
public override void ProcessMessage(object? sender, IConnectEventArgs args)
=> _dispatch.HandlePacket(args.Lease, args.Connection);
}
4. Client Integration (MyNet.Client)¶
The client uses the Nalix.SDK for high-level session management.
using Nalix.SDK.Transport;
using MyNet.Contracts;
// The client needs the same registry as the server
IPacketRegistry catalog = new PacketRegistryFactory()
.RegisterPacket<DataRequest>()
.RegisterPacket<DataResponse>()
.CreateCatalog();
using var session = new TcpSession(new TransportOptions { Address = "127.0.0.1", Port = 5000 }, catalog);
await session.ConnectAsync();
await session.HandshakeAsync();
var response = await session.RequestAsync<DataResponse>(new DataRequest
{
RequestId = 123,
Payload = "Hello Production"
});
Console.WriteLine($"Server said: {response.Message}");
5. Production Checklist¶
- [ ] Logging: Ensure
NLogixis configured with a high-performance sink (e.g.,BatchConsoleLogTarget). - [ ] Timeouts: Set
TimeoutMson all client calls viaRequestOptions. - [ ] Backpressure: Configure
NetworkSocketOptions.BacklogandDispatchOptions(MaxPerConnectionQueue,DropPolicy). - [ ] Health Checks: Use listener / hub / dispatch reports to monitor live sessions.
- [ ] Resource Cleanup: Ensure all
IBufferLeaseobjects are disposed (handled automatically if usingIPacketContext<T>).