Manual Server Wiring
On this page
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);
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.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 IFrameProcessor FrameProcessor => DefaultFrameProcessor.Instance;
public override IOpCodeExtractor OpCodeExtractor => DefaultOpCodeExtractor.Instance;
public override void ProcessMessage(object? sender, IConnectionEventArgs args)
=> _dispatch.HandlePacket(args.Lease, args.Connection);
}
5. Start the listener
public sealed class SampleTcpListener : TcpListenerBase
{
public SampleTcpListener(
ushort port,
IProtocol protocol,
IConnectionHub hub,
IConnectionGuard guard)
: base(port, protocol, hub, guard) { }
}
IConnectionHub hub = new ConnectionHub();
IConnectionGuard guard = new ConnectionGuard();
SampleTcpListener listener = new(57206, new SampleProtocol(dispatch), hub, guard);
listener.Activate();
TcpListenerBase.Activate(...) is synchronous. Use NetworkApplication.RunAsync() only when you want the hosting layer to own startup and shutdown for you.
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 responseThe 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(...)