NexNet.Generator 0.3.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package NexNet.Generator --version 0.3.3
NuGet\Install-Package NexNet.Generator -Version 0.3.3
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="NexNet.Generator" Version="0.3.3">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add NexNet.Generator --version 0.3.3
#r "nuget: NexNet.Generator, 0.3.3"
#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 NexNet.Generator as a Cake Addin
#addin nuget:?package=NexNet.Generator&version=0.3.3

// Install NexNet.Generator as a Cake Tool
#tool nuget:?package=NexNet.Generator&version=0.3.3

NexNet is a .NET real-time asynchronous networking library that provides bidirectional communication between a server and multiple clients.

Depends upon MemoryPack for message serialization. Internally packages Marc Gravell's Pipelines.Sockets.Unofficial with additional performance modifications for Pipeline socket transports.

Usage

Base classes
interface IInvocationSampleClientNexus
{
    ValueTask<string> GetUserName();
}

interface IInvocationSampleServerNexus
{
    void UpdateInfo(int userId, int status, string? customStatus);
    ValueTask UpdateInfoAndWait(int userId, int status, string? customStatus);
    ValueTask<int> GetStatus(int userId);
}


[Nexus<IInvocationSampleClientNexus, IInvocationSampleServerNexus>(NexusType = NexusType.Client)]
partial class InvocationSampleClientNexus
{
    public ValueTask<string> GetUserName()
    {
        return new ValueTask<string>("Bill");
    }
}

[Nexus<IInvocationSampleServerNexus, IInvocationSampleClientNexus>(NexusType = NexusType.Server)]
partial class InvocationSampleServerNexus
{
    private long _counter = 0;
    public void UpdateInfo(int userId, int status, string? customStatus)
    {
        // Do something with the data.
    }

    public ValueTask UpdateInfoAndWait(int userId, int status, string? customStatus)
    {
        // Do something with the data.
        if(_counter++ % 10000 == 0)
            Console.WriteLine($"Counter: {_counter}");

        return default;
    }

    public ValueTask<int> GetStatus(int userId)
    {
        return new ValueTask<int>(1);
    }
}
Usage
var client = InvocationSampleClientNexus.CreateClient(ClientConfig, new InvocationSampleClientNexus());
var server = InvocationSampleServerNexus.CreateServer(ServerConfig, () => new InvocationSampleServerNexus());

await server.StartAsync();
await client.ConnectAsync();

await client.Proxy.UpdateInfoAndWait(1, 2, "Custom Status");

Benchmarks

BenchmarkDotNet v0.13.8, Windows 11 (10.0.22621.2283/22H2/2022Update/SunValley2)
AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores
.NET SDK 8.0.100-rc.1.23455.8
  [Host]     : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2
  Job-ASQQIE : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2

Platform=X64  Runtime=.NET 7.0  MaxIterationCount=5
MaxWarmupIterationCount=3  MinIterationCount=3  MinWarmupIterationCount=1
Method Mean Error StdDev Op/s Gen0 Gen1 Allocated
InvocationNoArgument 38.66 us 2.070 us 0.537 us 25,867.9 0.0610 - 681 B
InvocationUnmanagedArgument 38.35 us 2.496 us 0.648 us 26,078.6 0.0610 - 737 B
InvocationUnmanagedMultipleArguments 38.98 us 2.044 us 0.531 us 25,654.8 0.0610 - 785 B
InvocationNoArgumentWithResult 38.22 us 0.752 us 0.195 us 26,166.8 0.0610 - 721 B
InvocationWithDuplexPipe_Upload 64.48 us 2.690 us 0.416 us 15,509.1 2.0752 0.4883 14142 B

Method Invocation Table

Some methods are handled differently based upon the arguments passed and there are limitations placed upon the types of arguments which can be used together. Most of these incompatibilities handled with Diagnostic Errors provided by the NexNet.Generator. Below is a table which shows valid combinations of arguments and return values.

CancellationToken NexusDuplexPipe Args
void X
ValueTask X X
ValueTask X X
ValueTask<T> X X

Notes:

  • CancellationTokens can't be combined with NexusDuplexPipe due to pipes having built-in cancellation/completion notifications.
  • CancellationToken must be at the end of the argument list like standard conventions.

Duplex Pipe Usage

NexNet has a limitation where the total arguments passed can't exceed 65,535 bytes. To address this, NexNet comes with built-in handling for duplex pipes via the NexusDuplexPipe argument, allowing you to both send and receive byte arrays. This is especially handy for continuous data streaming or when dealing with large data, like files. If you need to send larger data, you should use the NexusDuplexPipe arguments to handle the transmission.

Channels

Building upon the Duplex Pipes infrastructure, NexNet prvoides two channel structures to allow transmission/streaming of data structures via the INexusDuplexChannel<T> and INexusDuplexUnmanagedChannel<T> interfaces.

Several extension methods have been provided to allow for ease of reading and writing of entire collections (eg. selected table rows).

  • NexusChannelExtensions.WriteAndComplete<T>(...): Writing a collection to either a INexusDuplexChannel<T> or INexusChannelWriter<T> with optional batch sizes for optimized sending.
  • NexusChannelExtensions.ReadUntilComplete<T>(...): Reads from either a INexusDuplexChannel<T> or a INexusChannelReader<T> with an optional initial collection size to reduce collection resizing.
INexusDuplexChannel<T>

The INexusDuplexChannel<T> interface provides data transmission for all types which can be seralized by MemoryPack. This is the interface tuned for general usage and varying sized payloads. If you have an unmanaged types to send, make sure to use the INexusDuplexUnmanagedChannel<T> interface instead as it is fine tuned for performance of those simple types

Acquisition is handled through the INexusClient.CreateChannel<T> or SessionContext<T>.CreateChannel<T> methods. If an instance is created, it should be disposed to release held resources.

INexusDuplexUnmanagedChannel<T> (Unmanaged Types)

The INexusDuplexUnmanagedChannel<T> interface provides data transmission for unmanaged types. This is good for mainly simple types and structs. This interface should always be used over the INexusDuplexChannel<T> if the type is an unmanaged type as it is fine tuned for performance.

Acquisition is handled through the INexusClient.CreateUnmanagedChannel<T> or SessionContext<T>.CreateUnmanagedChannel<T> methods. If an instance is created, it should be disposed to release held resources.

Lifetimes

New hub instances are created for each session that connects to the hub. The hub manages the communication between the client and the server and remains active for the duration of the session. Once the session ends, either due to client disconnection, error or session timeout, the hub instance is automatically disposed of by NexNet.

Each session is assigned a unique hub instance, ensuring that data is not shared between different sessions. This design guarantees that each session is independently handled, providing a secure and efficient communication mechanism between the client and server.

Features

  • Automatic reconnection upon timeout or socket losing connection.
  • High performance Socket and Pipeline usage.
  • Multiple transports and easy extensibility.
  • Server ↔ Client communication
    • Cancellable Invocations
    • Proxies can return:
      • void for "fire and forget" invocation situations such as notifications.
      • ValueTask whcih waiting for invocation completion.
      • ValueTask<T> which will return a value from the remote invocation method.
  • Server can message multiple connected clients with a single invocation.
  • Automatic reconnection of clients upon timeout or loss of connection.
  • Thorough use of ValueTasks in hot paths for reduced invocation overhead.
  • Ping system to detect timeouts from cline tand server side.
  • No reflection. All hubs and proxies are created by the NexNet.Generator project. This allows for fast execution and easier tracing of bugs.
  • Full asynchronous TPL useage throughout socket reading/writing, processing and execution of invocations and their return values.
  • Minimal package requirements. MemoryPack

Transports Supported

  • Unix Domain Sockets (UDS)
  • TCP
  • TLS over TCP
  • QUIC (UDP)

Unix Domain Sockets are the most efficient as they encounter the least overhead and is a good candidate for inter process communication.

TCP allows for network and internet communication. Fastest option next to a UDS.

TLS over TCP allows for TLS encryption provided by the SslStream on both the server and client. This is still fast, but not as fast as either prior options as it creates a Socket, wrapped by a Network stream wrapped by a SslStream.

QUIC (UDP) s a UDP protocol which guarantees packet transmission, order and survives a connection IP and port change such as a connection switching from WiFi to celular. It requires the libmsquic library which can be installed on linux/unix based systems via the local app pacakge manager. Ubuntu: sudo apt install libmsquic. Must install the NexNet.Quic Nuget package to add the Quic transport.

Additional transports can be added easily as long as the transports guarantees order and transmission.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.8.0 189 1/12/2024
0.7.0 146 1/11/2024
0.6.0 189 11/29/2023
0.5.0 192 10/23/2023
0.4.0 164 9/17/2023
0.3.3 140 9/17/2023
0.3.2 152 9/13/2023
0.3.1 151 9/8/2023
0.3.0 175 9/7/2023
0.2.0 161 8/28/2023
0.1.0 134 5/10/2023