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:
1010

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
Feb 5, 2023
Memory leaks have long been programmer’s worst nightmare in .NET. When it comes to production servers, memory leaks are one of the most common and annoying issues. As we all know production servers must operate with the least amount of...
Mar 1, 2023
Author: Alper Ebiçoğlu
In this article, I’ll show you all the object mapping strategies of EF Core and especially the new one: TPC Inheritance Mapping. TPC mapping is the new feature introduced with Entity Framework Core 7. It’s called Table Per Concrete type...
Sep 10, 2023
Author: Sriram Kumar Mannava
In a situation where we need to modify our API’s structure or functionality while ensuring that existing API clients remain unaffected, the solution is versioning. We can designate our current APIs as the older version and introduce all intended changes...
Jun 27, 2023
Author: Anton Selin
Introduction Performance optimization is a key concern in software development, regardless of the programming language or platform you’re using. It’s all about making your software run faster or with less memory consumption, leading to better user experience and more efficient...
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