ThothRpc 0.7.5

dotnet add package ThothRpc --version 0.7.5
NuGet\Install-Package ThothRpc -Version 0.7.5
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ThothRpc" Version="0.7.5" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ThothRpc --version 0.7.5
#r "nuget: ThothRpc, 0.7.5"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ThothRpc as a Cake Addin
#addin nuget:?package=ThothRpc&version=0.7.5

// Install ThothRpc as a Cake Tool
#tool nuget:?package=ThothRpc&version=0.7.5

ThothRpc

ThothRpc is a drop-in, holistic, fully AOT compatible, lightweight, full duplex and bidirectional RPC framework for .Net. It's dead simple but powerful. It is completely platform agnostic and modular, making no assumptions of what kind of project you are building. The transport and object serialization (for parameters and method returns) layers are separate from the base library and custom implementation of these layers are easy to make giving you the freedom for you to easily build-your-own RPC system.

Of course, it wouldn’t be simple if these layers were not included for you. This library comes with a reliable-and-ordered UDP transport layer built off of LiteNetLib and a serialization layer built off of speedy Message Pack with a secure http/2 web based transport solution on the road map.

Build

NuGet NuGet

Usage Examples

To use the following sample code, you need these 3 nuget packages.

dotnet add package ThothRpc
dotnet add package ThothRpc.LiteNetLib
dotnet add package ThothRpc.MessagePack

--or--

Install-Package ThothRpc
Install-Package ThothRpc.LiteNetLib
Install-Package ThothRpc.MessagePack

Typed Rpc

Shared Code
public interface IClientService
{
    [ThothMethod] // indicates that this method is callable from server
    void PrintServerTime(DateTime time);

    Task GetHelloWorld();
}

public interface IServerService
{
    [ThothMethod] // indicates that this method is callable from client
    string GetHelloWorld();
}
Server
// hubs are thread-safe and can be single instanced for your entire app,
// or you can have multiple instances - its up to you
var hub = ServerHubBuilder.BuildServer()
    .UseTransport<LiteNetRpcServer>()
    .UseMessagePack() // any object that is serializable by MessagePack can be used in parameters or return values
    .Build();

var serverService = new ServerService(hub);

// register methods can be called multiple times to register multiple services to the same hub
hub.RegisterAs<IServerService>(serverService);
hub.Listen(9050, "SomeConnectionKey");

// Thread.Sleep(60000);
// hub.Dispose(); // closes the connection

public class ServerService : IServerService
{
    readonly ServerHub _hub;

    public ServerService(ServerHub hub)
    {
        _hub = hub;

        Task.Run(async () =>
        {
            // print the current time to all clients every second
            while (true)
            {
                var now = DateTime.Now;
                
                // Fire and forget
                _hub.InvokeForgetAllClients<IClientService>(DeliveryMode.Sequenced,
                    c => c.PrintServerTime(now));

                await Task.Delay(1000);
            }
        });
    }

    public string GetHelloWorld() // called from client
    {
        return "Hello World From Server!";
    }
}
Client
var hub = ClientHubBuilder.BuildClient()
    .UseTransport<LiteNetRpcClient>()
    .UseMessagePack()
    .Build();

var clientService = new ClientService(hub);
hub.RegisterAs<IClientService>(clientService);

await hub.ConnectAsync("localhost", 9050, "SomeConnectionKey");
await clientService.GetHelloWorld();

public class ClientService : IClientService
{
    readonly ClientHub _hub;

    public ClientService(ClientHub hub)
    {
        _hub = hub;
    }

    public async Task GetHelloWorld()
    {
        // Method invocations not using fire-and-forget with a udp transport are always delivered reliable and ordered.
        var helloWorld = await _hub.InvokeServerAsync<IServerService, string>
            (s => s.GetHelloWorld());

        Console.WriteLine(helloWorld);
    }

    public void PrintServerTime(DateTime time) // called from server
    {
        Console.WriteLine($"Server time: {time}");
    }
}

Typeless Rpc

The code below is the same as above, but this time without any strong typing.

Server
var hub = ServerHubBuilder.BuildServer()
    .UseTransport<LiteNetRpcServer>()
    .UseMessagePack()
    .Build();

var serverService = new ServerService(hub);
hub.Register(serverService, "ServerService");
hub.Listen(9050, "SomeConnectionKey");

public class ServerService
{
    readonly ServerHub _hub;

    public ServerService(ServerHub hub)
    {
        _hub = hub;

        Task.Run(async () =>
        {
            while (true)
            {
                _hub.InvokeForgetAllClients(DeliveryMode.Sequenced, 
                    "ClientService", "PrintServerTime", DateTime.Now);

                await Task.Delay(1000);
            }
        });
    }

    [ThothMethod]
    public string GetHelloWorld()
    {
        return "Hello World From Server!";
    }
}
Client
var hub = ClientHubBuilder.BuildClient()
    .UseTransport<LiteNetRpcClient>()
    .UseMessagePack()
    .Build();

var clientService = new ClientService(hub);
hub.Register(clientService, "ClientService");

await hub.ConnectAsync("localhost", 9050, "SomeConnectionKey");
await clientService.GetHelloWorld();

public class ClientService
{
    readonly ClientHub _hub;

    public ClientService(ClientHub hub)
    {
        _hub = hub;
    }

    public async Task GetHelloWorld()
    {
        var helloWorld = await _hub.InvokeServerAsync<string>
            ("ServerService", "GetHelloWorld");

        Console.WriteLine(helloWorld);
    }

    [ThothMethod]
    public void PrintServerTime(DateTime time)
    {
        Console.WriteLine($"Server time: {time}");
    }
}

Use Cases

Thoth (in its current state) is great for…

  • Game multiplayer networking
  • Ultra-fast reliable bi-directional microservice communication within a secured VPC
  • LAN/VPN based apps and tools

Note: Currently encrypted secured traffic is not yet a feature but will be present in the upcoming http/2 transport. However, implementing your own encryption system is easy with the ingress and egress callbacks.

Features

  • Runtime based
    • No contract files (.proto, ect)
    • Dynamic endpoint registration/unregistration
  • Performant
    • Low GC pressure design
    • Low CPU Usage
    • Small packet size (down to 3 bytes total for an optimized fire and forget call)
  • Various calling conventions
    • Typed or typeless invocation
    • In-process direct method calls when client and server are on the same machine
    • Fast fire-and-forget calling server or client with customizable delivery mode
    • Reliable RPC bi-directional request-response calling
  • Request handling customization for server and client separately
    • Manual handling allowing all incoming requests to be polled on a thread (i.e game-loop)
    • Multi-threaded thread pool handling of all incoming requests (like asp.net core)
  • Holistic and modular
    • Configurable transport and serialization
    • Configurable data ingress and egress
    • No-dependency logging (works with whatever you have, just use the callbacks)
    • No middleware tie-ins, required dependency injection configuration, or complicated boilerplate code
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 is compatible. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on ThothRpc:

Package Downloads
ThothRpc.LiteNetLib

Drop-in, holistic, lightweight, full duplex, bidirectional RPC framework for .Net

ThothRpc.MessagePack

Drop-in, holistic, lightweight, full duplex, bidirectional RPC framework for .Net

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.7.5 463 2/23/2023
0.7.4 536 1/3/2023
0.7.3 553 1/2/2023
0.7.1 275 12/21/2022
0.7.0 572 12/21/2022