Minimal Server (No Hosting)¶
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
TcpListenerBase - Send one request and receive one response
The sample stays intentionally small so you can copy the structure first and optimize later. Each step below maps directly to the runtime shape in src/Nalix.Network/Protocols/Protocol.PublicMethods.cs and src/Nalix.Hosting/NetworkApplicationBuilder.cs.
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();
The client uses the 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
IPacketContext<TPacket>when you need explicit manual sending or when you are working with custom packets - remember that the Listener handles raw frame transformation while the Protocol handles pure messages via
ProcessMessage(...)
Recommended Next Pages¶
- TCP Request/Response — Detailed TCP pattern guide
- Custom Middleware — Build middleware from scratch
- Packet Dispatch — Dispatch API reference
- Quickstart — The hosted-builder approach