Search  
Always will be ready notify the world about expectations as easy as possible: job change page
Apr 5

SignalR simplified: Creating efficient pure WebSocket servers with SimpleR for ASP.NET Core

SignalR simplified: Creating efficient pure WebSocket servers with SimpleR for ASP.NET Core
Author:
Source:
Views:
261

The growth of the internet has made instant communication technology more important than ever, especially for the Internet of Things (IoT). With so many devices like smart home gadgets and industrial sensors needing to talk to each other smoothly, having fast and reliable communication is crucial. This is where WebSockets come in. They’re great for quick, two-way chats between IoT devices and servers, helping everything stay connected and up-to-date in real time.

What is SignalR?

Real-time ASP.NET with SignalR | .NET (microsoft.com)
Real-time ASP.NET with SignalR | .NET (microsoft.com)

SignalR, which is now a part of the ASP.NET Core Framework, is the de-facto standard for creating real-time applications with .NET.

SignalR is a high-level framework in the ASP.NET Core suite designed for building real-time web applications. Using a custom protocol it abstracts the complexities of real-time communication, allowing developers to focus more on the application logic rather than the underlying protocols. A key strength of SignalR is its support for multiple transports for communication between clients and servers, ensuring that applications can maintain real-time functionality across different environments. These transports include:

  1. WebSockets, for full-duplex communication ideal for scenarios requiring high-frequency data exchange.
  2. Server-Sent Events (SSE), which allow servers to push updates to the client in scenarios where unidirectional communication is sufficient.
  3. Long Polling, a fallback mechanism that can be used when more advanced communication methods are not supported by the client or server environment.

By automatically selecting the best available transport method, SignalR ensures optimal functionality and performance for web applications across a wide range of devices and network conditions.

SignalR is a great framework for developing real-time web applications. However, its reliance on a specific protocol and client compatibility can introduce constraints, especially in scenarios where direct WebSocket communication is necessary.

SimpleR

SimpleR
vadrsa/SimpleR: High Performance Pure WebSocket Server Library based on SignalR (github.com)

Enter SimpleR, a high-performance, low overhead, pure WebSocket server library on top of ASP.NET Core. Designed to bridge the gap where SignalR might not be the optimal choice, SimpleR shines in simplicity and flexibility. It removes the heavier protocols and abstractions in favor of providing a straightforward, efficient means to implement pure WebSocket servers. This makes it an ideal candidate for projects requiring direct control over the WebSocket communication, custom protocol implementation or when working with non-SignalR clients(for example, when working with IoT devices with their own custom protocol).

In this introduction to SimpleR, we will explore its key features, and demonstrate how to seamlessly integrate with ASP.NET Core to enable the development of lightweight high-performance WebSocket servers.

Design philosophy

SimpleR is built mostly based on SignalR, building upon the incredible work done by the ASP.NET Core team. It is meant to provide

  1. High performance, low allocation abstraction over the network transport while being fully integrated with all ASP.NET Core features.
  2. Performance and minimal allocation is prioritized over ease of use.
  3. Protocol agnostic approach.

Setting up the environment

Before diving into the specifics of using SimpleR, you’ll need to start with the basics — creating an empty ASP.NET Core web application. This provides a clean slate for setting up your WebSocket server.

1. Open your command line interface (CLI): Navigate to the folder where you wish to create your project.

2. Create a new web project: Run the following command to generate an empty ASP.NET Core web application

dotnet new web -n SimpleThermostat.Server

3. Navigate to your project folder: Change directory to your newly created project folder

cd SimpleThermostat.Server

4. Install SimpleR.Server package: In your project directory, run the following command to add the SimpleR.Server package to your project. At the time of writing this article, SimpleR is still in alpha.

dotnet add package SimpleR.Server --version 1.0.0-alpha.1

Defining the protocol

Let’s start by defining client and server messages. To allow for different types of messages, we will be using the System.Text.Json polymorphic serialization feature.

The client, in this case the thermostat, will be sending metrics to the server.

[JsonDerivedType(typeof(ThermostatTemperatureMetric), "temperature")]
public class ThermostatMetric;

public class ThermostatTemperatureMetric : ThermostatMetric
{
    public ThermostatTemperatureMetric(float temperature)
    {
        Temperature = temperature;
    }

    public float Temperature { get; }
}

The server, on the other hand, will be sending commands to the thermostat.

[JsonDerivedType(typeof(SetThermostatModeCommand), "setMode")]
public class ThermostatCommand;

public class SetThermostatModeCommand : ThermostatCommand
{
    public SetThermostatModeCommand(ThermostatMode mode)
    {
        Mode = mode;
    }

    public ThermostatMode Mode { get; }
}

public enum ThermostatMode
{
    Off,
    Heat,
    Cool
}

Now that we have all the messages supported by our protocol, let’s define how they will be converted into bytes and vice-versa.

One of the key points of SimpleR, is that it is protocol-agnostic. Meaning that it requires the user to provide a protocol definition to be able to construct a message from the stream of bytes.

For this demo we will not go deep into defining our own custom protocol, but rather will use the WebSocket’s EndOfMessage as a delimiter for our messages.

To do that we need to define a pair of reader and writer classes.

// Since we are using WebSocket's EndOfMessage as a delimiter,
// need to implement the IDelimitedMessageReader interface
public class ThermostatMessageReader : IDelimitedMessageReader<ThermostatMetric>
{
    public ThermostatMetric ParseMessage(ref ReadOnlySequence<byte> input)
    {
        var jsonReader = new Utf8JsonReader(input);

        return JsonSerializer.Deserialize<ThermostatMetric>(ref jsonReader)!;
    }
}
public class ThermostatMessageWriter : IMessageWriter<ThermostatCommand>
{
    public void WriteMessage(ThermostatCommand message, IBufferWriter<byte> output)
    {
        var jsonWriter = new Utf8JsonWriter(output);
        JsonSerializer.Serialize(jsonWriter, message);
    }
}

Now that we have successfully defined our own message protocol, we are ready to move on to the application logic.

Dispatching messages

The next important topic in SimpleR is the Message Dispatcher. In a nutshell, a message dispatcher is a high-level pipeline that encapsulates the logic of where to dispatch connection messages. This is the initial entry into our application logic.

public class ThermostatMessageDispatcher : IWebSocketMessageDispatcher<ThermostatMetric, ThermostatCommand>
{
    private float _targetTemp = 22;
    
    public Task OnConnectedAsync(IWebsocketConnectionContext<ThermostatCommand> connection)
    {
        return Task.CompletedTask;
    }

    public Task OnDisconnectedAsync(IWebsocketConnectionContext<ThermostatCommand> connection, Exception? exception)
    {
        return Task.CompletedTask;
    }

    public async Task DispatchMessageAsync(IWebsocketConnectionContext<ThermostatCommand> connection, ThermostatMetric message)
    {
        if(message is ThermostatTemperatureMetric temperatureMetric)
        {
            // update temperature
            
            if (temperatureMetric.Temperature < _targetTemp)
            {
                // If the temperature is below the target temperature, set the thermostat to heat mode
                await connection.WriteAsync(new SetThermostatModeCommand(ThermostatMode.Heat));
            }
            else if (temperatureMetric.Temperature > _targetTemp)
            {
                // If the temperature is above the target temperature, set the thermostat to cool mode
                await connection.WriteAsync(new SetThermostatModeCommand(ThermostatMode.Cool));
            }
            else
            {
                // If the temperature is at the target temperature, turn off the thermostat
                await connection.WriteAsync(new SetThermostatModeCommand(ThermostatMode.Off));
            }
        }
    }
}

Connecting it all together

After we have defined our message protocol and message dispatcher, we are ready to connect it together in the Program.cs file.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSimpleR();

var app = builder.Build();

app.MapSimpleR<ThermostatMetric, ThermostatCommand>("thermostat/{deviceId}", b =>
{
    b.UseDispatcher<ThermostatMessageDispatcher>()
        .UseEndOfMessageDelimitedProtocol(
            MessageProtocol.From(new ThermostatMessageReader(), new ThermostatMessageWriter()));
});

app.Run();

It’s as simple as that!

You can find the source code of the server as well as a simple client here.

To explore all the features of SimpleR, check out the documentation in the Github repository. Don’t forget to star the repository if you found it useful.

Conclusion

As we wrap up our look into SimpleR, it’s clear that this library is a great choice for those who need direct control over their WebSocket communication in ASP.NET Core. Looking forward, we’ve got exciting plans to make SimpleR even better. We’re going to add ready-to-use protocols to make building apps easier and include special packages for widely-used standards like OCPP.

We’d love for you to join us and help make SimpleR better. Whether you have ideas, want to fix bugs, or just have something to say, we’re all ears. Your help and feedback are what make this project grow. And, if you like what SimpleR does and its approach to simplicity, please give our GitHub repository a star. It means a lot and helps others find us.

Similar
Jan 13, 2023
Author: Jaydeep Patil
We are going to discuss the Unit of Work design pattern with the help of a generic repository and step-by-step implementation using .NET Core 6 Web API.Agenda Repository Pattern Unit of Work ...
Mar 9, 2023
Author: Vithal Wadje
ASP.NET Core Blazor Server is a platform for developing modern and dynamic web applications. With Blazor Server, you can write code in C# and create rich user interfaces using HTML and CSS, all while taking advantage of server-side rendering and...
one week ago
Author: Mukesh Murugan
In this guide, we will learn how to implement Advanced Pagination in ASP.NET Core WebApi with ease. Pagination is one of the most important concepts while building RESTful APIs. You would have seen several public APIs implementing this feature for...
Mar 22
Author: Dayanand Thombare
IntroductionDelegates are a fundamental concept in C# that allow you to treat methods as objects. They provide a way to define a type that represents a reference to a method, enabling you to encapsulate and pass around methods as parameters,...
Send message
Email
Your name
*Message


© 1999–2024 WebDynamics
1980–... Sergey Drozdov
Area of interests: .NET Framework | .NET Core | C# | ASP.NET | Windows Forms | WPF | HTML5 | CSS3 | jQuery | AJAX | Angular | React | MS SQL Server | Transact-SQL | ADO.NET | Entity Framework | IIS | OOP | OOA | OOD | WCF | WPF | MSMQ | MVC | MVP | MVVM | Design Patterns | Enterprise Architecture | Scrum | Kanban