Starter Template¶
This page gives you a copy-paste server template for a new Nalix project.
It is intentionally opinionated:
- one startup file
- one protocol
- one listener
- one middleware
- one metadata provider
- one sample handler group
Use it as your first working skeleton, then split it into more files later.
Recommended folder structure¶
SampleServer/
Program.cs
Protocols/
SampleProtocol.cs
Listeners/
SampleTcpListener.cs
Middleware/
SampleAuditMiddleware.cs
Metadata/
SampleTenantMetadataProvider.cs
Handlers/
SamplePingHandlers.cs
Program.cs¶
using Nalix.Common.Diagnostics;
using Nalix.Common.Networking;
using Nalix.Common.Networking.Packets;
using Nalix.Framework.Configuration;
using Nalix.Framework.Injection;
using Nalix.Network.Configurations;
using Nalix.Network.Listeners.Tcp;
using Nalix.Network.Protocols;
using Nalix.Network.Routing;
NetworkSocketOptions socket = ConfigurationManager.Instance.Get<NetworkSocketOptions>();
socket.Validate();
DispatchOptions dispatchOptions = ConfigurationManager.Instance.Get<DispatchOptions>();
dispatchOptions.Validate();
ConnectionLimitOptions connectionLimitOptions =
ConfigurationManager.Instance.Get<ConnectionLimitOptions>();
connectionLimitOptions.Validate();
ConnectionHubOptions hubOptions = ConfigurationManager.Instance.Get<ConnectionHubOptions>();
hubOptions.Validate();
PoolingOptions pooling = ConfigurationManager.Instance.Get<PoolingOptions>();
pooling.Validate();
ILogger logger = BuildLogger();
IPacketRegistry packetRegistry = BuildPacketRegistry();
InstanceManager.Instance.Register(logger);
InstanceManager.Instance.Register(packetRegistry);
PacketMetadataProviders.Register(new SampleTenantMetadataProvider());
PacketDispatchChannel dispatch = new(options =>
{
options.WithLogging(logger)
.WithErrorHandling((ex, opcode) =>
{
logger.Error($"dispatch-error opcode=0x{opcode:X4}", ex);
})
.WithMiddleware(new SampleAuditMiddleware<IPacket>())
.WithHandler(() => new SamplePingHandlers());
});
SampleProtocol protocol = new(dispatch);
SampleTcpListener listener = new(socket.Port, protocol);
dispatch.Activate();
listener.Activate();
Console.WriteLine("Sample server started. Press ENTER to stop.");
Console.ReadLine();
listener.Deactivate();
dispatch.Dispose();
listener.Dispose();
static ILogger BuildLogger()
{
// Replace with your logger registration
throw new NotImplementedException();
}
static IPacketRegistry BuildPacketRegistry()
{
// Replace with your packet catalog registration
throw new NotImplementedException();
}
Protocols/SampleProtocol.cs¶
using Nalix.Common.Networking;
using Nalix.Network.Protocols;
using Nalix.Network.Routing;
public sealed class SampleProtocol : Protocol
{
private readonly PacketDispatchChannel _dispatch;
public SampleProtocol(PacketDispatchChannel dispatch)
{
_dispatch = dispatch;
this.SetConnectionAcceptance(true);
}
public override void ProcessMessage(object sender, IConnectEventArgs args)
=> _dispatch.HandlePacket(args.Lease, args.Connection);
protected override bool ValidateConnection(IConnection connection)
{
// Add IP or session admission checks here if needed
return true;
}
}
Listeners/SampleTcpListener.cs¶
using Nalix.Common.Networking;
using Nalix.Network.Listeners.Tcp;
public sealed class SampleTcpListener : TcpListenerBase
{
public SampleTcpListener(ushort port, IProtocol protocol) : base(port, protocol) { }
}
Middleware/SampleAuditMiddleware.cs¶
using Nalix.Common.Middleware;
using Nalix.Common.Networking.Packets;
using Nalix.Network.Middleware;
using Nalix.Network.Routing;
[MiddlewareOrder(-20)]
[MiddlewareStage(MiddlewareStage.Inbound)]
public sealed class SampleAuditMiddleware<TPacket> : IPacketMiddleware<TPacket>
where TPacket : IPacket
{
public async Task InvokeAsync(
PacketContext<TPacket> context,
Func<CancellationToken, Task> next)
{
ushort opcode = context.Attributes.PacketOpcode.OpCode;
Console.WriteLine(
$"opcode=0x{opcode:X4} endpoint={context.Connection.NetworkEndpoint}");
await next(context.CancellationToken);
}
}
Metadata/SampleTenantMetadataProvider.cs¶
using System.Reflection;
using Nalix.Network.Routing;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class PacketTenantAttribute : Attribute
{
public string Tenant { get; }
public PacketTenantAttribute(string tenant) => Tenant = tenant;
}
public sealed class SampleTenantMetadataProvider : IPacketMetadataProvider
{
public void Populate(MethodInfo method, PacketMetadataBuilder builder)
{
PacketTenantAttribute? attr = method.GetCustomAttribute<PacketTenantAttribute>();
if (attr is not null)
builder.Add(attr);
}
}
Handlers/SamplePingHandlers.cs¶
using Nalix.Common.Networking;
using Nalix.Common.Networking.Packets;
[PacketController("SamplePingHandlers")]
public sealed class SamplePingHandlers
{
[PacketOpcode(0x1001)]
[PacketTenant("core")]
public ValueTask<PingResponse> Handle(PingRequest request, IConnection connection)
{
PingResponse response = new()
{
Message = $"pong:{request.Message}"
};
return ValueTask.FromResult(response);
}
}
How to extend this template¶
Add more handlers¶
Create more handler classes and register them in one place:
options.WithHandler(() => new SamplePingHandlers())
.WithHandler(() => new SampleAccountHandlers())
.WithHandler(() => new SampleAdminHandlers());
Add stronger middleware¶
Common next additions:
- permission middleware
- timeout middleware
- rate limiting
- concurrency limiting
Add diagnostics¶
At minimum, keep access to:
listener.GenerateReport()protocol.GenerateReport()dispatch.GenerateReport()
First things to replace¶
When copying this template into a real project, replace:
BuildLogger()BuildPacketRegistry()PingRequest/PingResponsePacketTenantAttribute- the sample middleware logic
Those placeholders are there only to give your team a stable starting shape.