Dati binari in realtime con ASP.NET Core SignalR

di Moreno Gentili, in ASP.NET Core,

In un precedente articolo (https://www.aspitalia.com/articoli/asp.net-core/anteprima-aspnet-core-2-1-parte-2-p-2.aspx) abbiamo visto come usare ASP.NET Core SignalR per scambiare messaggi in tempo reale tra client e server.
Questa tecnologia, che sfrutta WebSockets per il trasporto dei dati, è ideale anche quando vogliamo monitorare da web lo stato di un sistema, come un macchinario industriale o un dispositivo IoT.

SignalR supporta lo scambio di messaggi sia testuali, come i parametri di funzionamento di una macchina, e sia binari, come le immagini catturate da una fotocamera.

In questo esempio vedremo appunto come usare ASP.NET Core SignalR per scambiare messaggi binari. Faremo in modo che l'applicazione, ad intervalli regolari, invii delle immagini ai client connessi. Il risultato che vogliamo ottenere è una pagina web che continui a visualizzare immagini, senza che debba essere ricaricata e senza inviare continue richieste ajax (polling).

Preparare il server

Iniziamo installando il seguente pacchetto NuGet che serve ad aggiungere il supporto a MessagePack.

dotnet add package Microsoft.AspNetCore.SignalR.Protocols.MessagePack

MessagePack, documentato all'indirizzo https://msgpack.org/, è un formato di serializzazione dei dati che somiglia al JSON ma che, a differenza del JSON, è molto più compatto e supporta anche dati binari senza che debbano essere codificati in Base64.


Grazie al pacchetto che abbiamo installato, ASP.NET Core SignalR si arricchisce di un protocollo di comunicazione che sfrutta MessagePack per ottimizzare la quantità di dati trasferiti tra server e client.

Il prossimo passo consiste nel creare un hub di ASP.NET Core SignalR che useremo per inviare le immagini da server a client. Creiamo dunque un file di codice Hubs/ImageStreamHub.cs e inseriamo al suo interno questa classe.

public class ImageStreamHub : Hub<IImageStreamClient>
{
  //Per questo esempio, qui non è necessario inserire nulla
}

Come si vede, questo hub deriva dalla classe base Hub, dove IImageStreamClient è un'interfaccia che ci permetterà di invocare funzioni sul client in maniera fortemente tipizzata. Ecco quindi il contenuto del file di codice Hubs/IImageStreamClient.cs.

public interface IImageStreamClient
{
  Task ReceiveImage(byte[] image);
}

Questa interfaccia definisce il metodo ReceiveImage che invocheremo lato server per inviare immagini al client. Come si nota, il suo paramento è di tipo byte[] che è idoneo per il contenuto binario di un'immagine.

Registrare i servizi di ASP.NET Core SignalR

A questo punto abbiamo preparato tutto il necessario e dobbiamo semplicemente registrare i servizi di ASP.NET Core SignalR. Quindi andiamo nel metodo ConfigureServices della classe Startup e aggiungiamo la seguente istruzione.

//Mettiamo questa riga in un qualsiasi punto nel metodo ConfigureServices
services.AddSignalR().AddMessagePackProtocol();

Come si vede, usando il metodo AddMessagePackProtocol abbiamo indicato di voler usare il protocollo di comunicazione basato su MessagePack.

Ora andiamo nel metodo Configure della classe Startup per usare il middleware di ASP.NET Core SignalR e indicare l'indirizzo a cui il client potrà collegarsi al nostro hub.

//Aggiungiamo questa riga prima di app.UseMvc
app.UseSignalR((configure) =>
{
  configure.MapHub<ImageStreamHub>("/image-stream", options => {
    //Limite di peso dell'immagine da inviare al client
    options.ApplicationMaxBufferSize = 256 * 1024; //256KB
  });
});

In questo punto è anche importante valorizzare l'opzione ApplicationMaxBufferSize per indicare il peso massimo che un'immagine può assumere.

Inviare immagini al client

Gli hub di ASP.NET Core SignalR sono ben integrati con la dependency injection di ASP.NET Core e quindi, nei componenti della nostra applicazione, possiamo ricevere il servizio IHubContext che ci permette di invocare funzioni sul client. Ecco un esempio in cui riceviamo il servizio dal costruttore di un hosted service.

public class ImageGenerator : BackgroundService
{
  private readonly IHubContext<ImageStreamHub, IImageStreamClient> hubContext;
  
  //Riceviamo il servizio IHubContext<THub, T> nel costruttore
  public ImageGenerator(IHubContext<ImageStreamHub, IImageStreamClient> hubContext)
  {
    this.hubContext = hubContext;
  }
  
  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    //Questo hosted service invia ai client un'immagine ogni secondo
  //finché l'applicazione non viene arrestata
    while(!stoppingToken.IsCancellationRequested)
    {
      byte[] imageData = GetImageData();
    //Possiamo inviare un messaggio ai client in maniera fortemente tipizzata
    //invocando il metodo ReceiveImage che avevamo definito nell'interfaccia
      await hubContext.Clients.All.ReceiveImage(imageData);
    //Attendiamo un secondo e poi il ciclo ricomincia
      await Task.Delay(1000, stoppingToken);
    }
  }
  
  private byte[] GetImageData()
  {
    //TODO: Qui logica per leggere il contenuto dell'immagine
  //ad esempio da una cartella in cui la webcam la salva
  }
}

Per chiarimenti sugli hosted service, si veda un precedente script all'indirizzo https://www.aspitalia.com/script/1276/Operazioni-Background-Hosted-Service-ASP.NET-Core.aspx

Se usiamo un hosted service, ricordiamoci di registrarlo dal metodo ConfigureServices della classe Startup.

services.AddHostedService<ImageGenerator>();

Nel prossimo script...

Per ora abbiamo solo visto come preparare la parte server dell'applicazione ASP.NET Core. Nel prossimo script vedremo quale codice JavaScript usare nella parte client per ricevere e visualizzare le immagini inviate dal server.
Il codice dell'applicazione dimostrativa è già pubblicato nel repository GitHub https://github.com/aspitalia/aspnetcore-signalr-binary

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi