{"id":28441528,"url":"https://github.com/royben/resonance","last_synced_at":"2025-07-20T16:04:31.948Z","repository":{"id":39450818,"uuid":"346538982","full_name":"royben/Resonance","owner":"royben","description":"High Performance Real-Time C# Communication Library.","archived":false,"fork":false,"pushed_at":"2021-06-25T15:35:48.000Z","size":154018,"stargazers_count":47,"open_issues_count":2,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-07-07T11:43:40.826Z","etag":null,"topics":["communication-library","csharp","named-pipes","network","resonance","signalr","socket","tcp","transport","udp","usb","webrtc"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/royben.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-11T01:13:28.000Z","updated_at":"2025-03-26T03:31:49.000Z","dependencies_parsed_at":"2022-09-01T19:10:33.392Z","dependency_job_id":null,"html_url":"https://github.com/royben/Resonance","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/royben/Resonance","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royben%2FResonance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royben%2FResonance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royben%2FResonance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royben%2FResonance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/royben","download_url":"https://codeload.github.com/royben/Resonance/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royben%2FResonance/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266152279,"owners_count":23884479,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["communication-library","csharp","named-pipes","network","resonance","signalr","socket","tcp","transport","udp","usb","webrtc"],"created_at":"2025-06-06T05:00:48.115Z","updated_at":"2025-07-20T16:04:31.941Z","avatar_url":"https://github.com/royben.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/royben/Resonance/blob/dev/visuals/Logo.png?raw=true\" /\u003e    \n\u003c/p\u003e\n\n# Resonance \u003cimg width=\"20\" height=\"20\" src=\"https://github.com/royben/Resonance/blob/dev/visuals/icon.png?raw=true\" /\u003e [![Build Status](https://dev.azure.com/Sirilix/Resonance/_apis/build/status/Resonance%20Release?branchName=main)](https://dev.azure.com/Sirilix/Resonance/_build/latest?definitionId=3\u0026branchName=main) ![Issues](https://img.shields.io/github/issues/royben/Resonance.svg) ![Azure DevOps tests](https://img.shields.io/azure-devops/tests/Sirilix/Resonance/3) ![Nuget](https://img.shields.io/nuget/dt/Resonance) ![GitHub](https://img.shields.io/github/license/royben/Resonance)\n\nResonance is a high-performance real-time C# communication library with built-in support for several different transcoding and delivery methods.\nThis library provides an intuitive API for asynchronous communication between machines and devices by exposing a set of easy to use, pluggable components.\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n| Module | Nuget | Description | Frameworks\n|:---------------------------------------------|:----------|:--------|:--------|\n| Resonance | [![Nuget](https://img.shields.io/nuget/v/Resonance)](https://www.nuget.org/packages/Resonance/) \u003cimg width=30/\u003e | Core components | .NET Standard 2.0 |\n| Resonance.Protobuf | [![Nuget](https://img.shields.io/nuget/v/Resonance.Protobuf)](https://www.nuget.org/packages/Resonance.Protobuf/) | Protobuf Encoder \u0026 Decoder | .NET Standard 2.0 |\n| Resonance.MessagePack | [![Nuget](https://img.shields.io/nuget/v/Resonance.MessagePack)](https://www.nuget.org/packages/Resonance.MessagePack/) | MessagePack Encoder \u0026 Decoder | .NET Standard 2.0 |\n| Resonance.USB | [![Nuget](https://img.shields.io/nuget/v/Resonance.USB)](https://www.nuget.org/packages/Resonance.USB/) | USB Adapter support.  | .NET 4.6.1, .NET 5, Xamarin.Forms |\n| Resonance.SignalR | [![Nuget](https://img.shields.io/nuget/v/Resonance.SignalR)](https://www.nuget.org/packages/Resonance.SignalR/) | SignalR (core and legacy) Adapters and Hubs. | .NET Standard 2.0 |\n| Resonance.WebRTC | [![Nuget](https://img.shields.io/nuget/v/Resonance.WebRTC)](https://www.nuget.org/packages/Resonance.WebRTC/) | WebRTC Adapter support. | .NET Standard 2.0 |\n| Resonance.LZ4 | [![Nuget](https://img.shields.io/nuget/v/Resonance.LZ4)](https://www.nuget.org/packages/Resonance.LZ4/) | LZ4 Compression support. | .NET Standard 2.0 |\n| Resonance.Bluetooth | [![Nuget](https://img.shields.io/nuget/v/Resonance.Bluetooth)](https://www.nuget.org/packages/Resonance.Bluetooth/) | Bluetooth Communication Adapter, Server and Discovery. | .NET 4.6.1, .NET 5, Xamarin.Forms |\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n## Overview\nResonance is a feature-rich communication framework, designed to bring complex, real-time communication methodologies, down to a solid, intuitive API that will provide you with all the tooling you need, out of the box.\nGet started implementing advanced communication applications with minimal effort.\n\n\u003cbr/\u003e\n\nResonance provides support for the following key features.\n* ✅ **One-way messages** - send a message from one side to another with or without acknowledgement.\n* ✅ **Request-Response messages** - send a request while expecting a response.\n* ✅ **Continuous response streams** - send a single request and receive multiple response messages.\n* ✅ **Integrated encryption, compression and keep alive mechanism.**\n* ✅ **Built-in support for RPC** - use a common interface for communication by calling standard methods, properties and events!\n* ✅ **Built-in servers and services for handling incoming connections.**\n* ✅ **Built-in support for various communication methods** -whether you want to communicate between applications on the same device, on the local area network or across the internet.\n* ✅ **Built-in support for various encoding and decoding interfaces**.\n* ✅ **Standard and Fluent APIs for clear construction of objects**.\n* ✅ **Support for both synchronous and asynchronous operations.**\n* ✅ **Output communication logs to any logging library of your choice.**\n* ✅ **Work with standard C# objects and types without the need to handle all the bits and bytes.**\n\n\u003cbr/\u003e\n\n🔗 [Check out the Wiki page for tutorials and fully working demos!](https://github.com/royben/Resonance/wiki)\n\n\u003cbr/\u003e\n\nThe resonance library might be described by these 3 basic layers:\n\n### Transporting\nA `Transporter` responsibility is to provide the API for sending and receiving messages, managing those messages, and propagating the required information to other components.\n\n### Transcoding\nEncoders and Decoders are components that can be plugged to a transporter, they determine how outgoing/incoming messages should be encoded and whether the data should be encrypted and/or compressed.\nThe Following built-in transcoding methods are currently supported by the library:\n*\tJson\n*\tBson\n*\tProtobuf\n*\tMessagePack\n*\tXml\n\n### Adapters\nAdapters can also be plugged to a transporter to determine how outgoing/incoming encoded data is going to be transmitted and where.\nThe following built-in adapters are currently supported by the library:\n*\tTCP\n*\tUDP\n*\tUSB\n*\tIn-Memory\n*\tSignalR\n*\tWebRTC\n*\tBluetooth\n*\tNamed Pipes\n*\tShared Memory\n\n\u003cbr/\u003e\n\nThe following diagram described a simple request-response scenario.\n\n![alt tag](https://github.com/royben/Resonance/blob/dev/visuals/Resonance_Flow.png?raw=true)\n\n\u003cbr/\u003e\n\n## Getting Started\nThe first step is deciding about the communication and transcoding methods that we are going to use.\nFor this demonstration, we are going to use JSON transcoding over TCP/IP as the means of communication between two Transporters.\n\nHere is how we can create the first Transporter with JSON encoding/decoding and TCP/IP adapter.\n```c#\npublic async void Init()\n{\n    IResonanceTransporter transporter = new ResonanceTransporter();\n\n    transporter.Adapter = new TcpAdapter(\"127.0.0.1\", 8888);\n    transporter.Encoder = new JsonEncoder();\n    transporter.Decoder = new JsonDecoder();\n\n    await transporter.ConnectAsync();\n}\n```\n\u003cbr/\u003e\n\nA Transporter can also be instantiated using the fluent syntax builder.\n```c#\npublic async void Init()\n{\n    IResonanceTransporter transporter = ResonanceTransporter.Builder\n        .Create()\n        .WithTcpAdapter()\n        .WithAddress(\"127.0.0.1\")\n        .WithPort(8888)\n        .WithJsonTranscoding()\n        .Build();\n\n    await transporter.ConnectAsync();\n}\n```\n\u003cbr/\u003e\n\nNow, since we are using TCP/IP as the means of communication, we need a TCP server/listener to accept incoming connections.\u003cbr/\u003e\nFor that, we are going to use the built-in `ResonanceTcpServer` class.\nAlthough, you can use any other TCP/IP listener.\n\nHere, we are going to create a new TCP server and wait for incoming connections.\u003cbr/\u003e\nOnce a new connection is available, the `ConnectionRequest` event will be triggered.\u003cbr/\u003e\nThe event arguments contains the `Accept` and `Decline` methods for accepting or declining the new connection.\u003cbr/\u003e\nThe accept method returns an initialized `TcpAdapter` that can be used to create the \"other side\" second Transporter.\n```c#\npublic async void Init_TcpServer()\n{\n    ResonanceTcpServer server = new ResonanceTcpServer(8888);\n    server.ConnectionRequest += Server_ConnectionRequest;\n    await server.StartAsync();\n}\n\nprivate async void Server_ConnectionRequest(object sender, ResonanceListeningServerConnectionRequestEventArgs\u003cTcpAdapter\u003e e)\n{\n    IResonanceTransporter transporter2 = ResonanceTransporter.Builder\n        .Create()\n        .WithAdapter(e.Accept()) //Call the Accept method to get a new TcpAdapter.\n        .WithJsonTranscoding()\n        .Build();\n        \n    await transporter2.ConnectAsync();\n}\n```\n\u003cbr/\u003e\n\nNow that we have both transporters connected, we can start sending messages.\u003cbr/\u003e\nLet's define two simple request and response messages.\u003cBr/\u003e\n\n*CalculateRequest*:\n```c#\npublic class CalculateRequest\n{\n    public double A { get; set; }\n    public double B { get; set; }\n}\n```\n\u003cbr/\u003e\n\n*CalculateResponse*:\n```c#\npublic class CalculateResponse\n{\n    public double Sum { get; set; }\n}\n```\n\u003cbr/\u003e\n\nHere is how we can send a `CalculateRequest` from the first Transporter, while expecting a `CalculateResponse` from the \"other-side\" second Transporter.\n```c#\nvar response = await transporter1.SendRequestAsync\u003cCalculateRequest, CalculateResponse\u003e(new CalculateRequest()\n{\n    A = 10,\n    B = 5\n});\n\nConsole.WriteLine(response.Sum);\n```\n\u003cBr/\u003e\n\nFinally, we need to handle the incoming `CalculateRequest` on the second Transporter.\u003cbr/\u003e\n\nHandling incoming requests can be achieved using any of the following method:\n- Registering a `RequestReceived` event handler.\n- Registering a request handler using the `RegisterRequestHandler` method.\n- Using remote procedure calls. (see the **RPC** section on this document) \n\nWe are going to cover the first two methods.\n\n\u003cbr/\u003e\n\n**Handling incoming requests using the `RequestReceived` event:**\n\u003cBr/\u003e\n\nLet's go back to where we accepted the first Transporter connection and initialized the second one.\n\u003cBr/\u003e\n\nHere is how we would register for the `RequestReceived` event and respond to the `CalculateRequest` with a `CalculateResponse`.\n```c#\nprivate async void Server_ConnectionRequest(object sender, ResonanceListeningServerConnectionRequestEventArgs\u003cTcpAdapter\u003e e)\n{\n    IResonanceTransporter transporter2 = ResonanceTransporter.Builder\n        .Create()\n        .WithAdapter(e.Accept())\n        .WithJsonTranscoding()\n        .Build();\n    \n    //Register an event handler..\n    transporter2.RequestReceived += Transporter2_RequestReceived;\n\n    await transporter2.ConnectAsync();\n}\n\nprivate async void Transporter2_RequestReceived(object sender, ResonanceRequestReceivedEventArgs e)\n{\n    if (e.Message.Object is CalculateRequest calculateRequest)\n    {\n        if (calculateRequest.A \u003e 0 \u0026\u0026 calculateRequest.B \u003e 0)\n        {\n            await e.Transporter.SendResponseAsync(new CalculateResponse()\n            {\n                Sum = calculateRequest.A + calculateRequest.B\n            }, e.Request.Token);\n        }\n        else\n        {\n            await e.Transporter.SendErrorResponseAsync(\"A \u0026 B must be greater than zero\", e.Request.Token);\n        }\n    }\n}\n```\n\u003cBr/\u003e\n\nNotice, when using this method, we need to explicitly call the Transporter `SendResponse` method while specifying the request token.\u003cbr/\u003e\nIf there are any errors, we can trigger an exception on the other-side Transporter by calling the `SendErrorResponse` method.\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n**Handling incoming request using a `Request Handler`:**\n\u003cbr/\u003e\n\nA better and more intuitive approach is to register a request handler method that will require far less coding, filtering and error handling.\n\n\u003cBr/\u003e\n\nLet's go back again and see how we might register a request handler.\n\n```c#\nprivate async void Server_ConnectionRequest(object sender, ResonanceListeningServerConnectionRequestEventArgs\u003cTcpAdapter\u003e e)\n{\n    IResonanceTransporter transporter2 = ResonanceTransporter.Builder\n        .Create()\n        .WithAdapter(e.Accept())\n        .WithJsonTranscoding()\n        .Build();\n    \n    //Register a request handler method...\n    transporter2.RegisterRequestHandler\u003cCalculateRequest, CalculateResponse\u003e(HandleCalculateRequest);\n\n    await transporter2.ConnectAsync();\n}\n\n\nprivate ResonanceActionResult\u003cCalculateResponse\u003e HandleCalculateRequest(CalculateRequest request)\n{\n    if (request.A \u003e 0 \u0026\u0026 request.B \u003e 0)\n    {\n        return new CalculateResponse() { Sum = request.A + request.B };\n    }\n    else\n    {\n        throw new ArgumentException(\"A \u0026 B must be greater than zero\");\n    }\n}\n```\n\u003cBr/\u003e\n\nNotice, when using this method, we don't need to specify the request token,\u003cbr/\u003e\nand, we are just returning a `CalculateResponse` as the result of the method.\nAlso, we don't need to explicitly report any errors, we can just throw an exception.\nActually, any exception that occurs while handling a request, will trigger an automatic error response.\n\n\u003cBr/\u003e\n\n**Congratulations!** we have successfully completed a fully working request-response pattern.\n\nYou can continue reading if you want to explore some more advanced topics.\n\n\u003cbr/\u003e\n\n## In-Memory Testing\nTesting your communication is easier without initializing an actual known communication method. The library implements a special `InMemoryAdapter` which can be used for testing.\u003cbr/\u003e\nAll you need to do is assign each of the adapters the same address.\n\n```c#\npublic async void Demo()\n{\n    IResonanceTransporter transporter1 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TEST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    IResonanceTransporter transporter2 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TEST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    await transporter1.ConnectAsync();\n    await transporter2.ConnectAsync();\n}\n```\n\n\u003cbr/\u003e\n\n## Continuous Response\nThe continuous response pattern is simply the concept of sending a single request and expecting multiple response messages.\u003cbr/\u003e\nWe are basically opening a constant stream of response messages.\u003cbr/\u003e\nThis pattern is useful if we want to track some state of a remote application.\u003cbr/\u003e\n\nIn this example, we are going to track a simple made up progress.\u003cbr/\u003e\nThe first Transporter will send a `ProgressRequest` using the `SendContinuousRequest` method.\u003cbr/\u003e\n\nContinuous request tracking is made using the Reactive programming style. Meaning, the request sender will need to `Subscribe` and provide `Next`, `Error` and `Completed` callbacks.\n\nFirst, let's create our `ProgressRequest` and `ProgressResponse` messages.\n\n*ProgressRequest*:\n```c#\npublic class ProgressRequest\n{\n    public int Count { get; set; }\n    public TimeSpan Interval { get; set; }\n}\n```\n\u003cbr/\u003e\n\n*ProgressResponse*:\n```c#\npublic class ProgressResponse\n{\n    public int Value { get; set; }\n}\n```\n\u003cbr/\u003e\n\nNow, we are going to initialize two Transporters, send a continuous request from the first one, and respond with multiple response messages from the other.\n\n```c#\npublic async void Send_Continuous_Request()\n{\n    IResonanceTransporter transporter1 = ResonanceTransporter.Builder\n       .Create()\n       .WithInMemoryAdapter()\n       .WithAddress(\"TEST\")\n       .WithJsonTranscoding()\n       .Build();\n\n    IResonanceTransporter transporter2 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TEST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    await transporter1.ConnectAsync();\n    await transporter2.ConnectAsync();\n\n    transporter2.RegisterRequestHandler\u003cProgressRequest\u003e(async (t, request) =\u003e\n    {\n        for (int i = 0; i \u003c request.Message.Count; i++)\n        {\n            await t.SendResponseAsync(new ProgressResponse() { Value = i }, request.Token);\n            Thread.Sleep(request.Message.Interval);\n        }\n    });\n\n    transporter1.SendContinuousRequest\u003cProgressRequest, ProgressResponse\u003e(new ProgressRequest()\n    {\n        Interval = TimeSpan.FromSeconds(1),\n        Count = 10\n    }).Subscribe((response) =\u003e\n    {\n        Console.WriteLine(response.Value);\n    }, (ex) =\u003e\n    {\n        Console.WriteLine($\"Error: {ex.Message}\");\n    }, () =\u003e\n    {\n        Console.WriteLine($\"Continuous Request Completed!\");\n    });\n}\n```\n\n\u003cbr/\u003e\n\n\n## One-way Messages\nIn all previous examples we have seen how we can send a request and receive a response in various ways.\u003cbr/\u003e\nActually, you can also send a message without expecting any response by using the `Send` method.\n\u003cbr/\u003e\n\nHere is how you might send a message with no response and handle it on the other side.\n```c#\nprivate async void Send_Example()\n{\n    IResonanceTransporter t1 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    IResonanceTransporter t2 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    t1.Connect();\n    t2.Connect();\n\n    t2.RegisterMessageHandler\u003cSomeObject\u003e((t, message) =\u003e \n    {\n        Console.WriteLine(message.Object.SomeValue);\n    });\n\n    await t1.SendAsync(new SomeObject() { SomeValue = \"some value\"});\n}\n```\n\u003cBr/\u003e\n\nNotice, there is a distinct separation between requests and one-way messages methods, events and handling.\u003cbr/\u003e\n\n* One-way messages are sent using the `Send` method and not the `SendRequest` method.\n* One-way messages are received using the `MessageReceived` event and not the `RequestReceived` event.\n* One-way messages handlers are registered using the `RegisterMessageHandler` and not the `RegisterRequestHandler`.\n\n\u003cbr/\u003e\n\nBy default, one-way message is sent with no acknowledgment, meaning, there is no certainty that it was received by the other side, but you can configure a one-way message to require an acknowledgment.\u003cbr/\u003e\nTo do this, you need to set the message configuration `RequireACK` property to `true`.\n```c#\nawait t1.SendAsync(new SomeObject() { SomeValue = \"some value\"}, new ResonanceMessageConfig() \n{\n     RequireACK = true\n});\n```\nWhen setting `RequireACK = true`, the `SendAsync` will wait until an acknowledgment is received.\u003cbr/\u003e\n\nOne-way message acknowledgment can also carry any error that might occur at the receiving side and throw an exception .\nTo enable this functionality, you need to configure both transporters `MessageAcknowledgmentBehavior` property to `ResonanceMessageAckBehavior.ReportErrors`.\u003cbr/\u003e\n\nHere is an example:\n```c#\nprivate async void SendWithErrorExample()\n{\n    IResonanceTransporter t1 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    IResonanceTransporter t2 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TST\")\n        .WithJsonTranscoding()\n        .Build();\n    \n    //Enable error reporting.\n    t1.MessageAcknowledgmentBehavior = ResonanceMessageAckBehavior.ReportErrors;\n    t2.MessageAcknowledgmentBehavior = ResonanceMessageAckBehavior.ReportErrors;\n\n    t1.Connect();\n    t2.Connect();\n\n    t2.RegisterMessageHandler\u003cSomeObject\u003e((t, message) =\u003e \n    {\n        throw new Exception(\"Test Error\");\n    });\n\n    try\n    {\n        await t1.SendAsync(new SomeObject() { SomeValue = \"some value\" }, new ResonanceMessageConfig()\n        {\n            RequireACK = true\n        });\n    }\n    catch (Exception ex)\n    {\n        Console.WriteLine(ex.Message);\n    }\n}\n```\n\n\n\u003cbr/\u003e\n\n\n## Message Configuration\nIn all previous example we used the Transporter to send request and response messages by providing the request or response objects, but actually, we can specify additional configuration to each request or response message.\u003cbr/\u003e\n\nFor example, by specifying a request configuration, we can change the default timeout for the request, or priority of the message.\u003cbr/\u003e\n\nHere is how you would specify the configuration of a simple request message.\n```c#\nvar response = await transporter1.SendRequest\u003cCalculateRequest, CalculateResponse\u003e(new CalculateRequest()\n{\n    A = 10,\n    B = 5\n}, new ResonanceRequestConfig()\n{\n    //After 5 seconds and no response, a TimeoutException will be thrown.\n    Timeout = TimeSpan.FromSeconds(5),\n    \n    //This message has high priority in the message queue.\n    Priority = Threading.QueuePriority.High,\n});\n```\nA continuous request configuration also allows specifying the continuous timeout, meaning, the maximum time interval between each response.\n\n\u003cbr/\u003e\n\n## Keep Alive\nThe Resonance library implements an automatic internal keep alive mechanism.\u003cbr/\u003e\nThe keep alive mechanism helps detect lost connection between adapters.\u003cbr/\u003e\n\nWe can enable/disable and control a Transporter's keep alive behavior by changing its `KeepAliveConfiguration` property.\u003cbr/\u003e\n\nHere is how we would change a Transporter's keep alive configuration.\n```c#\nIResonanceTransporter t = new ResonanceTransporter();\n\n//Enable keep alive.\nt.KeepAliveConfiguration.Enabled = true;\n\n//Frequency of keep alive messages.\nt.KeepAliveConfiguration.Interval = TimeSpan.FromSeconds(5);\n\n//Maximum failed attempts.\nt.KeepAliveConfiguration.Retries = 4;\n\n//Respond to the other-side transporter keep alive messages.\nt.KeepAliveConfiguration.EnableAutoResponse = true;\n\n//Transporter state will change to 'Failed' when keep alive times out.\nt.KeepAliveConfiguration.FailTransporterOnTimeout = true;\n```\n\u003cbr/\u003e\n\nHere is how you can configure the keep alive using the Transporter fluent builder.\n```c#\nIResonanceTransporter transporter1 = ResonanceTransporter.Builder\n    .Create()\n    .WithTcpAdapter()\n    .WithAddress(\"127.0.0.1\")\n    .WithPort(8888)\n    .WithJsonTranscoding()\n    .WithKeepAlive(interval: TimeSpan.FromSeconds(5), retries: 4)\n    .Build();\n```\n\u003cbr/\u003e\n\n\n## Compression\nThe Resonance transcoding system provides the ability to compress messages and reduce network bandwidth.\u003cBr/\u003e\nCompression and decompression is performed by the Transporter's `Encoder` and `Decoder`.\nTo enable the encoder compression, you can access the encoder's `CompressionConfiguration`.\n\n```c#\nIResonanceTransporter t = new ResonanceTransporter();\nt.Encoder = new JsonEncoder();\nt.Encoder.CompressionConfiguration.Enabled = true; //Enable compression.\n```\n\u003cbr/\u003e\n\nOr, using the fluent builder...\n\n```c#\nIResonanceTransporter transporter1 = ResonanceTransporter.Builder\n    .Create()\n    .WithInMemoryAdapter()\n    .WithAddress(\"TST\")\n    .WithJsonTranscoding()\n    .NoKeepAlive()\n    .NoEncryption()\n    .WithCompression() //Enable compression\n    .Build();\n```\n\u003cbr/\u003e\n\nOnce the Encoder is configured for compression, all sent messages will be compressed.\u003cbr/\u003e\nThere is no need to configure the receiving Decoder as it automatically detects the compression from the message header.\u003cbr/\u003e\n\nThe base library uses GZip for compression, but you can use the faster LZ4 compression algorithm by installing the **[Resonance.LZ4](https://www.nuget.org/packages/Resonance.LZ4)** nuget package and specifying it as the Encoder's compressor.\n```c#\nIResonanceTransporter transporter1 = ResonanceTransporter.Builder\n    .Create()\n    .WithInMemoryAdapter()\n    .WithAddress(\"TST\")\n    .WithJsonTranscoding()\n    .NoKeepAlive()\n    .NoEncryption()\n    .WithLZ4Compression() //Enable LZ4 compression.\n    .Build();\n```\n\u003cbr/\u003e\n\n## Encryption\nThe Resonance transcoding system also provides the ability to encrypt and decrypt outgoing and incoming messages.\u003cbr/\u003e\nAlthough some of the adapters already supports their internal encryption like the SignalR and WebRTC adapters, you might want to implement your own.\u003cbr/\u003e\n\nThe Resonance library implements its own automatic SSL style encryption using a handshake that is initiated in order to exchange encryption information.\u003cbr/\u003e\nThe handshake negotiation is done by the `IHandshakeNegotiator` interface.\u003cbr/\u003e\n\nIn order to secure a communication channel, each participant needs to create an `Asymmetric` RSA private-public key pair, then share that public key with the remote peer.\u003cbr/\u003e\nNext, both participants needs to agree on a `Symmetric` encryption configuration based on a shared password.\u003cbr/\u003e\nThe actual password is shared and encrypted using the initial RSA public key.\u003cbr/\u003e\n\nOnce the password is acquired by both participants, they can start send and receive messages using the faster `Symmetric` encryption based on the shared password.\n\n\u003cbr/\u003e\n\n![alt tag](https://github.com/royben/Resonance/blob/dev/visuals/Resonance_Encryption.png?raw=true)\n\n\n\u003cbr/\u003e\n\nAfter we understand what happens behind the scenes, let's see how to configure a Transporter to encrypt messages.\n\n```c#\nIResonanceTransporter t = new ResonanceTransporter();\nt.CryptographyConfiguration.Enabled = true; //Enable encryption.\n```\n\u003cbr/\u003e\n\nYou can also configure encryption using the fluent builder.\n\n```c#\nIResonanceTransporter transporter1 = ResonanceTransporter.Builder\n    .Create().WithTcpAdapter()\n    .WithAddress(\"127.0.0.1\")\n    .WithPort(8888)\n    .WithJsonTranscoding()\n    .WithKeepAlive()\n    .WithEncryption() //Enable encryption.\n    .Build();\n```\n\u003cbr/\u003e\n\nIn order to establish a secure communication, both Transporters `CryptographyConfiguration` must be enabled.\u003cbr/\u003e\n\nIn case encryption is disabled on both transporters, no handshake will occur at all.\n\n\u003cbr/\u003e\n\n## Logging\nIt is very important to be able to trace your communication through logs.\u003cbr/\u003e\nThe Resonance library takes advantage of structured logs by attaching log properties\nthat can later be used to trace and aggregate each request and response path.\u003cBr/\u003e\n\nCommunication logs are delivered using Microsoft's Logging.Abstractions interfaces.\u003c/br\u003e\nThat makes it easy to hook up your favorite logging library to expose Resonance communication logs.\u003cbr/\u003e\n\nIn order to route Resonance logging to your logging infrastructure, you need to provide an instance of `ILoggerFactory` .\u003cbr/\u003e\n\nHere is how you can hookup Resonance to **[Serilog](https://github.com/serilog/serilog)** logging library.\n```c#\npublic void InitLogging()\n{\n    var loggerFactory = new LoggerFactory();\n    var logger = new LoggerConfiguration()\n        .MinimumLevel.Debug()\n        .WriteTo.Console()\n        .WriteTo.Seq(\"http://localhost:5341\")\n        .CreateLogger();\n\n    loggerFactory.AddSerilog(logger);\n\n    ResonanceGlobalSettings.Default.LoggerFactory = loggerFactory;\n}\n```\n\u003cbr/\u003e\n\nOnce you have your logging configured, you can also specify each request logging mode through the `RequestConfig` object.\u003cbr/\u003e\n\nThe logging mode of a request determines whether the request and its response should be logged and how.\u003cbr/\u003e\n\n- `None` (will not log the request)\n- `Title` (logs the request and its response message name)\n- `Content` (logs the request and its response message name along with the actual message content)\n\nNote, when the minimum log level is \"Debug\" request and response messages will always be logged in `Title` mode, unless the `Content` logging mode was specified.\n\n\u003cbr/\u003e\n\nHere is how you would configure the logging mode of a request.\n\n```c#\npublic async void Demo()\n{\n    IResonanceTransporter transporter1 = ResonanceTransporter.Builder\n       .Create().WithTcpAdapter()\n       .WithAddress(\"127.0.0.1\")\n       .WithPort(8888)\n       .WithJsonTranscoding()\n       .WithKeepAlive()\n       .NoEncryption()\n       .WithCompression()\n       .Build();\n\n    await transporter1.ConnectAsync();\n\n    CalculateRequest request = new CalculateRequest() { A = 10, B = 5 };\n\n    //Log request and response names\n    var response = await transporter1.SendRequestAsync\u003cCalculateRequest, CalculateResponse\u003e(request,\n        new ResonanceRequestConfig() { LoggingMode = ResonanceMessageLoggingMode.Title });\n\n    //Log request and response names and content\n    response = await transporter1.SendRequestAsync\u003cCalculateRequest, CalculateResponse\u003e(request,\n        new ResonanceRequestConfig() { LoggingMode = ResonanceMessageLoggingMode.Content });\n}\n```\n\n\u003cbr/\u003e\n\nThe recommended way of viewing the Resonance communication logs is using  [Seq](https://datalust.co/) with the [Serilog](https://github.com/serilog/serilog) Seq sink.\u003cbr/\u003e\nThe Seq logs viewer supports structured logs that fits nicely with Resonance logging implementation.\u003cbr/\u003e\n\nHere is a screenshot of a request being traced using its `Token` property through Seq.\u003cbr/\u003e\n![alt tag](https://github.com/royben/Resonance/blob/dev/visuals/Seq.png?raw=true)\n\n\u003cbr/\u003e\n\n## RPC (Remote Procedure Call)\nRemote procedure call is basically executing a method on another machine as if it was on your machine. The Resonance framework provides a very advanced built-in RPC mechanism with support for methods, properties and even events!\nThis is done by defining a common interface between the caller and the callee (server-client), implementing the interface on the server side, registering it with a transporter then creating a client proxy on the client side.\n\nThe following is a short example of an RPC implementation.\u003cbr/\u003e\nFirst let's create the common interface.\n\n```c#\npublic interface ICalcService\n{\n    event EventHandler\u003cEventArgs\u003e CalculationCompleted;\n    int TotalCalculations { get; }\n    double Add(double a, double b);\n    double Subtract(double a, double b);\n    Task\u003cdouble\u003e AddAsync(double a, double b);\n    Task\u003cdouble\u003e SubtractAsync(double a, double b);\n}\n```\n\u003cbr/\u003e\n\nNow, let's create the server implementation.\n\n```c#\npublic class CalcServiceImpl : ICalcService\n{\n    public int TotalCalculations { get; private set; }\n\n    public event EventHandler\u003cEventArgs\u003e CalculationCompleted;\n\n    public double Add(double a, double b)\n    {\n        OnCalculationCompleted();\n        return a + b;\n    }\n\n    public Task\u003cdouble\u003e AddAsync(double a, double b)\n    {\n        return Task.FromResult(Add(a, b));\n    }\n\n    public double Subtract(double a, double b)\n    {\n        OnCalculationCompleted();\n        return a - b;\n    }\n\n    public Task\u003cdouble\u003e SubtractAsync(double a, double b)\n    {\n        return Task.FromResult(Subtract(a, b));\n    }\n\n    protected virtual void OnCalculationCompleted()\n    {\n        TotalCalculations++;\n        CalculationCompleted?.Invoke(this, new EventArgs());\n    }\n}\n```\n\u003cbr/\u003e\n\nAll we need to do now is to register the implementation with the server Transporter and create a client proxy using the client Transporter.\u003cbr/\u003e\n\nHere is an example.\n\n```c#\npublic async void TestRPC()\n{\n    //Server transporter\n    IResonanceTransporter t1 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    //Client transporter\n    IResonanceTransporter t2 = ResonanceTransporter.Builder\n        .Create()\n        .WithInMemoryAdapter()\n        .WithAddress(\"TST\")\n        .WithJsonTranscoding()\n        .Build();\n\n    await t1.ConnectAsync();\n    await t2.ConnectAsync();\n\n    //Register the calc service with the server transporter.\n    t1.RegisterService\u003cICalcService, CalcServiceImpl\u003e(RpcServiceCreationType.Singleton);\n\n    //Create the client proxy using the client transporter.\n    ICalcService client = t2.CreateClientProxy\u003cICalcService\u003e();\n\n    client.CalculationCompleted += (sender, e) =\u003e \n    {\n        //Event should be raised here!\n        Debug.WriteLine(client.TotalCalculations);\n    };\n\n    //Execute a remote method and get the result.\n    var result = await client.AddAsync(10, 15);\n}\n```\n\u003cbr/\u003e\n\nThat simple! no pre generated code is required, the client proxy code is generated at runtime.\n\nNow, as we have seen in previous examples, when sending a request, it is possible to customize some aspects like logging and timeouts.\nWhen using RPC, you can achieve that by attaching the `ResonanceRpc` attribute for a method or property.\nFor example, if we would want to specify a custom timeout for the \"Add\" method on our interface we could do that.\n\n```c#\n[ResonanceRpc(Timeout = 5)]\ndouble Add(double a, double b);\n```\n\n\u003cbr/\u003e\n\n## Performance Benchmarks\n\u003e1000 Roundtrips (request -\u003e response), Intel Core i7-6700HQ CPU 2.60GHz (Skylake)\n\n**Transcoding**\n\n|                                       Method |     Mean |   Error |  StdDev |\n|--------------------------------------------- |---------:|--------:|--------:|\n| Json | 205.6 ms | 4.09 ms | 9.96 ms |\n| Protobuf | 180.3 ms | 4.07 ms | 3.81 ms |\n\n\u003cbr/\u003e\n\n**Encryption / Compression**\n\n|                                                              Method |     Mean |   Error |   StdDev |\n|-------------------------------------------------------------------- |---------:|--------:|---------:|\n| Normal | 183.4 ms | 2.87 ms |  2.40 ms |\n| Compressed | 421.9 ms | 8.25 ms | 13.32 ms |\n| Encrypted | 260.9 ms | 5.18 ms | 12.41 ms |\n| Compressed / Encrypted | 517.2 ms | 9.12 ms |  8.08 ms |\n\n*more benchmarks will be added soon..*","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froyben%2Fresonance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froyben%2Fresonance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froyben%2Fresonance/lists"}