Dignus.ActorServer 3.0.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package Dignus.ActorServer --version 3.0.3
                    
NuGet\Install-Package Dignus.ActorServer -Version 3.0.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="Dignus.ActorServer" Version="3.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Dignus.ActorServer" Version="3.0.3" />
                    
Directory.Packages.props
<PackageReference Include="Dignus.ActorServer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Dignus.ActorServer --version 3.0.3
                    
#r "nuget: Dignus.ActorServer, 3.0.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.
#:package Dignus.ActorServer@3.0.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Dignus.ActorServer&version=3.0.3
                    
Install as a Cake Addin
#tool nuget:?package=Dignus.ActorServer&version=3.0.3
                    
Install as a Cake Tool

Dignus.ActorServer

NuGet

High-performance actor-based network server framework.


Overview

Dignus.ActorServer combines:

  • Actor-based execution (Dignus.Actor.Core)
  • High-performance networking (TCP / TLS)
  • Protocol-based message dispatch

Incoming network packets are decoded into actor messages and processed sequentially.


Quick Start

A minimal server consists of:

  1. Register protocol mappings
  2. Configure server options (decoder / serializer)
  3. Create actor system
  4. Implement session actor
  5. Implement server
  6. Start server

1. Protocol Registration

private static void RegisterProtocol(IServiceProvider serviceProvider)
{
    var mapper = serviceProvider.GetService<ProtocolBodyTypeMapper>();

    mapper.AddMapping<CreateAccountReq>(CLSProtocol.CreateAccountReq);
    mapper.AddMapping<LoginReq>(CLSProtocol.LoginReq);
}

2. Server Setup

RegisterProtocol(serviceProvider);

var serverOptions = ServerOptions.Builder()
    .UseDecoder(new PacketFramer(serviceProvider))
    .UseSerializer(new MessageSerializer())
    .Build();

var actorSystem = new ActorSystem();

var lobby = new LobbyServer(actorSystem, serverOptions, serviceProvider);
lobby.Start(30000);

3. Packet Decoder

internal class PacketFramer(IServiceProvider serviceProvider) : IActorMessageDecoder
{
    protected const int PacketSize = sizeof(int);
    protected const int ProtocolSize = sizeof(ushort);

    private readonly ProtocolBodyTypeMapper _bodyTypeMapper =
        serviceProvider.GetService<ProtocolBodyTypeMapper>();

    public IActorMessage Deserialize(ReadOnlySpan<byte> packet)
    {
        int protocol = BitConverter.ToUInt16(packet[..PacketSize]);

        if (_bodyTypeMapper.ContainsProtocol(protocol) == false)
            return null;

        var bodyString = Encoding.UTF8.GetString(packet[ProtocolSize..]);
        var bodyType = _bodyTypeMapper.GetBodyType(protocol);

        return (IActorMessage)JsonSerializer.Deserialize(bodyString, bodyType);
    }

    public bool TryFrame(ISession session, ArrayQueue<byte> buffer,
        out ArraySegment<byte> packet,
        out int consumedBytes)
    {
        packet = default;
        consumedBytes = 0;

        if (buffer.Count < PacketSize)
            return false;

        var packetSize = BitConverter.ToInt32(buffer.Peek(PacketSize));

        if (buffer.Count < packetSize + PacketSize)
            return false;

        consumedBytes = BitConverter.ToInt32(buffer.Read(PacketSize));
        return buffer.TrySlice(out packet, consumedBytes);
    }
}

4. Serializer

internal class MessageSerializer : IActorMessageSerializer
{
    public IActorMessage Deserialize(ArraySegment<byte> bytes)
    {
        return new BinaryMessage(bytes);
    }

    public ArraySegment<byte> MakeSendBuffer(INetworkActorMessage packet)
    {
        return packet is BinaryMessage msg ? msg.Data : default;
    }

    public ArraySegment<byte> MakeSendBuffer(IPacket packet)
    {
        return default;
    }
}

5. Session Actor

public class PlayerActor : SessionActorBase
{
    private readonly StateController _stateController;
    private Player _player;

    public PlayerActor(IServiceProvider serviceProvider)
    {
        _stateController = new StateController(this, serviceProvider);
    }

    protected override ValueTask OnReceive(IActorMessage message, IActorRef sender)
    {
        if (message is KickUserMessage kick)
        {
            Send(PacketFactory.MakePacket(LSCProtocol.ServerNotify, 0,
                new ServerNotify { ErrorCode = kick.Reason }));

            ChangeState(PlayerStateType.Disconnect);
        }
        else
        {
            return _stateController.HandleMessageAsync(message, sender);
        }

        return ValueTask.CompletedTask;
    }

    public void Send(INetworkActorMessage message)
    {
        NetworkSession.SendAsync(message);
    }

    public void Send(IPacket packet)
    {
        NetworkSession.SendAsync(packet);
    }
}

6. Server Implementation

internal class LobbyServer(
    ActorSystem actorSystem,
    ServerOptions serverOptions,
    IServiceProvider serviceProvider)
    : TcpServerBase<PlayerActor>(actorSystem, serverOptions)
{
    protected override PlayerActor CreateSessionActor()
    {
        var actor = new PlayerActor(serviceProvider);
        actor.ChangeState(PlayerStateType.Initial);
        return actor;
    }

    protected override void OnDeadLetterMessage(DeadLetterMessage deadLetterMessage)
    {
        LogHelper.Error($"{deadLetterMessage.Reason}");

        if (deadLetterMessage.Reason == DeadLetterReason.ExecutionException)
        {
            var exceptionMessage = deadLetterMessage.Message as ActorExceptionMessage;
            LogHelper.Error(exceptionMessage.Exception);
        }
    }
}

Execution Flow

TCP Packet
    ↓
Frame
    ↓
Deserialize (protocol → message)
    ↓
Actor Mailbox
    ↓
Actor.OnReceive
    ↓
Send Response

Performance

Benchmark Environment

  • CPU: Intel Core i5-12400F (12th Gen)
  • Cores / Threads: 6 / 12
  • Max Turbo Frequency: 4.40 GHz
  • Memory: 32 GB
  • Runtime: .NET 10 (Release x64)

Test Conditions

  • Server address: 127.0.0.1
  • Server port: 5000
  • Protocol: Plain TCP (no TLS)
  • Working clients: 1
  • In-flight messages per client: 1000
  • Message size: 32 bytes
  • Benchmark duration: 10 seconds

Round-Trip (TCP)

Total Time: 10.002 seconds
Total Client: 1
Total Bytes: 4,364,075,744
Total Data: 4.06 GiB
Total Message: 136,377,367
Data Throughput: 416.11 MiB/s
Message Throughput: 13,634,960 msg/s

Test Conditions

  • Server address: 127.0.0.1
  • Server port: 5000
  • Protocol: Plain TCP (no TLS)
  • Working clients: 100
  • Message size: 32 bytes
  • Benchmark duration: 10 seconds

Fan-out (100 clients)

Total Time: 10.008 seconds
Total Client: 100
Total Bytes: 4,909,594,848
Total Data: 4.57 GiB
Total Message: 153,424,839
Data Throughput: 467.83 MiB/s
Message Throughput: 15,329,728 msg/s

Test Conditions

  • Server address: 127.0.0.1
  • Server port: 5000
  • Protocol: TLS over TCP
  • Working clients: 1
  • Message size: 32 bytes
  • Benchmark duration: 10 seconds

TLS Round-Trip

Total Time: 10.011 seconds
Total Client: 1
Total Bytes: 3,531,230,304
Total Data: 3.29 GiB
Total Message: 110,350,947
Data Throughput: 336.41 MiB/s
Message Throughput: 11,023,486 msg/s

Test Conditions

  • Server address: 127.0.0.1
  • Server port: 5000
  • Protocol: TLS over TCP
  • Working clients: 100
  • Message size: 32 bytes
  • Benchmark duration: 10 seconds

TLS Fan-out (100 clients)

Total Time: 10.052 seconds
Total Client: 100
Total Bytes: 4,852,865,632
Total Data: 4.52 GiB
Total Message: 151,652,051
Data Throughput: 460.42 MiB/s
Message Throughput: 15,087,042 msg/s

Highlights

  • 10M+ messages/sec throughput
  • 300–450 MiB/s sustained throughput
  • full end-to-end measurement (decode → actor → encode → send)
  • no ThreadPool usage for actor execution

Architecture Overview

TcpServerBase / TlsServerBase
        ↓
ActorPacketProcessor
        ↓
SessionActor
        ↓
Actor

Concurrency Model

  • single-threaded execution per actor
  • dispatcher-based scheduling
  • no shared mutable state
  • message-driven processing

Protocol Model (Default)

Protocol → BodyType → Deserialize → Actor
  • no handler binding
  • minimal overhead
  • direct actor execution

Protocol Pipeline (Advanced)

Protocol → Middleware → Handler → Actor

Use when:

  • authentication required
  • validation needed
  • logging / filtering required
  • complex execution flow needed

TCP / TLS Support

  • TCP → TcpServerBase<TActor>
  • TLS → TlsServerBase<TActor>

Usage is identical except transport layer.


Summary

  • Actor → business logic
  • ActorSystem → execution
  • Server → network entry point
  • Decoder → packet parsing
  • Serializer → packet writing
  • Mapper → protocol resolution

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Dignus.ActorServer:

Package Downloads
Dignus.Commands

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.1.0 134 5/8/2026
3.0.4 121 5/2/2026
3.0.3 98 5/2/2026
3.0.2 107 4/29/2026
3.0.1 103 4/23/2026
3.0.0 137 4/16/2026
3.0.0-preview.9 55 4/11/2026
3.0.0-preview.8 64 4/9/2026
3.0.0-preview.7 56 3/30/2026
3.0.0-preview.6 57 3/29/2026
3.0.0-preview.5 55 3/29/2026
3.0.0-preview.4 84 3/28/2026
3.0.0-preview.3 53 3/23/2026
3.0.0-preview.2 48 3/22/2026
3.0.0-preview.1 45 3/22/2026
2.0.1 98 3/21/2026
2.0.0 109 3/20/2026
1.2.3 113 3/19/2026
1.2.2 110 3/18/2026
1.2.1 125 3/15/2026
Loading failed