A library for cross-process method calls over TCP. Designed for calling .NET methods from Elixir (see hexdocs.pm/netler), and usable anywhere you need fast binary RPC between processes. All messages are serialised with MessagePack.
dotnet add package Netler.NETusing Netler;
var server = Server.Create(config =>
{
config.UsePort(5544);
config.UseRoutes(routes =>
{
routes.Add("Add", param =>
{
var a = Convert.ToInt32(param[0]);
var b = Convert.ToInt32(param[1]);
return a + b;
});
});
});
await server.Start();AddTyped infers parameter and return types from the lambda. The C# compiler enforces the types; the wire format is unchanged.
config.UseRoutes(routes =>
{
routes.AddTyped("Add", (int a, int b) => a + b);
routes.AddTyped("Double", (int x) => x * 2);
routes.AddTyped("Ping", () => "pong");
routes.AddTyped("Log", (string msg) => { /* void handler */ });
});Params.Decode wraps a typed delegate into the Func<object[], object> signature that IRoutes.Add expects. Useful when you want to separate the handler from the route registration, or pass an existing method reference.
static int Add(int a, int b) => a + b;
config.UseRoutes(routes =>
{
routes.Add("Add", Params.Decode<int, int, int>(Add));
routes.Add("Double", Params.Decode((int x) => x * 2));
routes.Add("Ping", Params.Decode(() => "pong"));
});Any type annotated with [MessagePackObject] can be used as a parameter or return value.
using MessagePack;
[MessagePackObject]
public class EchoRequest { [Key(0)] public string Text { get; set; } }
[MessagePackObject]
public class EchoResponse { [Key(0)] public string Message { get; set; }
[Key(1)] public int Length { get; set; } }
config.UseRoutes(routes =>
{
routes.AddTyped("Echo", (EchoRequest req) =>
new EchoResponse { Message = req.Text, Length = req.Text.Length });
});The server can watch a client OS process and react automatically when it exits.
Server.Create(config =>
{
config.UsePort(5544);
config.UseClientPid(clientProcessId);
config.UseClientDisconnectBehaviour(ClientDisconnectBehaviour.DisposeServer);
});ClientDisconnectBehaviour |
Effect |
|---|---|
ShutdownApplication |
Calls Environment.Exit(0) — default |
DisposeServer |
Stops the server; application keeps running |
KeepAlive |
No action |
using Microsoft.Extensions.Logging;
ILogger logger = loggerFactory.CreateLogger<MyApp>();
Server.Create(config =>
{
config.UsePort(5544);
config.UseLogger(logger);
config.UseRoutes(routes => { /* ... */ });
});using Netler;
using var client = new Client(5544);
var sum = await client.InvokeAsync<int>("Add", new object[] { 2, 3 });
var pong = await client.InvokeAsync<string>("Ping", new object[0]);
// Complex return type — EchoResponse must carry [MessagePackObject]
var reply = await client.InvokeAsync<EchoResponse>("Echo",
new object[] { new EchoRequest { Text = "hello" } });var raw = await client.InvokeAsync("Add", new object[] { 2, 3 });
var sum = Convert.ToInt32(raw);using var client = new Client("192.168.1.10", 5544);When a route handler throws an unhandled exception on the server, the client receives a RemoteInvokationFailed exception containing the server-side error message.
using Netler.Exceptions;
try
{
await client.InvokeAsync("Divide", new object[] { 10, 0 });
}
catch (RemoteInvokationFailed ex)
{
Console.WriteLine($"Server error: {ex.Message}");
}