Minimal Server Guide¶
Low-Level Implementation
This guide demonstrates how to manually wire the Nalix runtime without the NetworkApplication hosting builder. This is considered an advanced topic and is only recommended for specialized transport libraries or low-level performance tuning.
For 99% of applications, use the Hosting Builder or Server Boilerplate.
Learning Signals
- Level: Advanced
- Time: 10–15 minutes
- Prerequisites: Quickstart
The steps are:
- Register shared services
- Build a packet dispatcher
- Forward messages from
Protocolinto dispatch - Start a custom
TcpListenerBase - Send one request and receive one response
The sample stays intentionally small so you can copy the structure first and optimize later.
Server¶
1. Register shared services¶
InstanceManager.Instance.Register<ILogger>(logger);
InstanceManager.Instance.Register<IPacketRegistry>(packetRegistry);
2. Create handlers¶
[PacketController("SamplePingHandlers")]
public sealed class SamplePingHandlers
{
[PacketOpcode(0x1001)]
public ValueTask<Control> Ping(IPacketContext<Control> request)
{
request.Packet.Type = ControlType.PONG;
return ValueTask.FromResult(request.Packet);
}
}
3. Build the dispatcher¶
PacketDispatchChannel dispatch = new(options =>
{
options.WithLogging(logger)
.WithHandler(() => new SamplePingHandlers());
});
dispatch.Activate();
4. Bridge protocol to dispatch¶
public sealed class SampleProtocol : Protocol
{
private readonly PacketDispatchChannel _dispatch;
public SampleProtocol(PacketDispatchChannel dispatch) => _dispatch = dispatch;
public override void ProcessMessage(object? sender, IConnectEventArgs args)
=> _dispatch.HandlePacket(args.Lease, args.Connection);
}
5. Start the listener¶
public sealed class SampleTcpListener : TcpListenerBase
{
public SampleTcpListener(ushort port, IProtocol protocol, IConnectionHub hub)
: base(port, protocol, hub) { }
}
IConnectionHub hub = new ConnectionHub();
SampleTcpListener listener = new(57206, new SampleProtocol(dispatch), hub);
listener.Activate();
TcpListenerBase.Activate(...) is synchronous. Use NetworkApplication.RunAsync() only when you want the hosting layer to own startup and shutdown for you.
Manual hub ownership
Current TcpListenerBase constructors require an IConnectionHub. The hosting
builder creates and wires this dependency for you. In manual mode, create the
hub explicitly and dispose it when your server shuts down.
Client¶
The client uses Nalix.SDK to connect and perform type-safe request/response operations:
using Contracts;
using Nalix.SDK.Transport.Extensions;
// 1. Establish connection
await session.ConnectAsync();
// 2. Request/Response in one line
Control response = await session.RequestAsync<Control>(
new Control { Type = ControlType.PING },
options: RequestOptions.Default.WithTimeout(3_000)
);
Console.WriteLine(response.Type); // PONG
Full flow¶
sequenceDiagram
participant Client
participant Listener as TcpListenerBase
participant Protocol as SampleProtocol
participant Dispatch as PacketDispatchChannel
participant Handler as SamplePingHandlers
Client->>Listener: TCP frame
Listener->>Protocol: connection message event
Protocol->>Dispatch: HandlePacket(lease, connection)
Dispatch->>Handler: Ping(request)
Handler-->>Dispatch: Control
Dispatch-->>Client: serialized response
The same end-to-end structure works with a custom packet type if you replace Control with your own packet contract in the handler and client sample.
What to customize next¶
- add middleware
- add packet attributes such as timeout, permission, or rate limit
- switch some handlers to
PacketContext<TPacket>when you need explicit manual sending or when you are working with custom packets - remember that the Listener handles raw frame transformation (Pipeline) while the Protocol handles pure messages via
ProcessMessage(...)