Nello script precedente (https://www.aspitalia.com/script/1327/Dati-Binari-Realtime-ASP.NET-Core-SignalR.aspx) abbiamo implementato la parte server di un'applicazione web che, grazie ad ASP.NET Core SignalR, invia immagini in tempo reale ai client connessi. Questo esempio è utile se dobbiamo monitorare le immagini catturate da una webcam oppure ricevere altro tipo di dati binari (o anche testuali) da dispositivi industriali o IoT.
Preparare il client
Ora continuiamo sviluppando la parte client, in cui andremo a ricevere i dati binari dal server e a visualizzarli in una pagina HTML. Per prima cosa aggiungiamo i riferimenti ai file JavaScript necessari.- Il client di ASP.NET Core SignalR (pacchetto npm @aspnet/signalr), che espone le funzionalità basilari per stabilire una connessione bidirezionale con il server;
- L'implementazione del protocollo MessagePack (pacchetto npm msgpack5) che contiene la logica di serializzazione dei messaggi in formato binario;
- Il supporto a MessagePack per SignalR (pacchetto npm @aspnet/signalr-protocol-msgpack).
Queste dipendenze client possiamo procurarcele in vari modi: usando libman (https://www.aspitalia.com/script/1304/Usare-LibMan-Gestire-Dipendenze-Client-ASP.NET-Core.aspx), npm, oppure semplicemente referenziando i file da un CDN come jsdelivr, proprio come mostrato in questo esempio. Possiamo aggiungere questo codice dove preferiamo nel corpo della pagina HTML o ad esempio nella @section Scripts di una view Razor.
<script src="https://cdn.jsdelivr.net/npm/@aspnet/signalr@1.1.4/dist/browser/signalr.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/msgpack5@4.2.1/dist/msgpack5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@aspnet/signalr-protocol-msgpack@1.1.0/dist/browser/signalr-protocol-msgpack.min.js"></script>
Ora che abbiamo referenziato i file necessari, aggiungiamo di seguito il JavaScript per configurare la connessione all'hub di ASP.NET Core SignalR.
<script> const connection = new signalR.HubConnectionBuilder() .withUrl("/image-stream") //Url del nostro hub SignalR (spiegato nel precedente articolo) .withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol()) //Usiamo il protocollo binario basato su MessagePack .build(); </script>
Ora dobbiamo registrarci per ricevere i messaggi 'ReceiveImage' inviati dal server. Nel farlo, indichiamo una funzione di callback che verrà automaticamente invocata ogni volta che il server invia una nuova immagine da visualizzare. La callback riceve i dati binari in forma di Uint8Array (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), un oggetto JavaScript che rappresenta un array di byte.
Ottenuto l'array di byte potremmo certamente leggerne il contenuto come un comune array. Nel nostro caso, dato che abbiamo a che fare con delle immagini, si pone il problema di come visualizzarle nella pagina web. A tale scopo dobbiamo incapsulare l'array di byte in un oggetto Blob (https://developer.mozilla.org/en-US/docs/Web/API/Blob) da cui potremo ottenere un percorso assegnabile alla proprietà src di un elemento img.
<script> let previousObjectURL; connection.on('ReceiveImage', data => { //La variabile data è di tipo Uint8Array //Creo un blob a partire dai dati binari che ho ricevuto const blob = new Blob([data], {type: "image/png"}); //Creo un percorso al Blob e lo assegno alla proprietà src di un elemento img const currentObjectURL = URL.createObjectURL(blob); document.getElementById('targetImage').src = currentObjectURL; //Dealloco l'URL generato precedentemente, così da evitare dei memory leak. if (previousObjectURL) { URL.revokeObjectURL(previousObjectURL); } //Conservo il riferimento all'URL che ho generato poco fa, così che lo si possa deallocare poi previousObjectURL = currentObjectURL; }); </script>
Ora siamo pronti per stabilire una connessione all'hub di ASP.NET Core SignalR. A quel punto cominceremo a ricevere le immagini dal server.
<script> connection.start(); </script>
Non dimentichiamo di aggiungere alla pagina l'elemento img che visualizzerà le immagini. Il suo attributo src può essere inizializzato con un'immagine qualsiasi, perché poi verrà riassegnato con i percorsi ai Blob creati di volta in volta.
<img id="targetImage" src="/blank.gif" width="200" height="200">
Provare il funzionamento
Se avviamo l'applicazione, vedremo una nuova immagine apparire ogni secondo. Andando a curiosare nella scheda "Network" o "Rete" degli strumenti di sviluppo del browser (tasto F12), vedremo apparire tante richieste, una per ogni immagine mostrata. Come si vede qui, il browser crea i percorsi ai Blob con un formato particolare, contenente un codice che lo aiuta a identificare univocamente la risorsa binaria presente in memoria.
Inoltre, se usiamo un web debugger come Fiddler, vedremo che i dati binari sono traferiti tali e quali, senza alcuna codifica Base64, dato che MessagePack è appunto un protocollo che supporta il trasferimento binario.

Conclusioni
Grazie ad ASP.NET Core SignalR e al trasporto MessagePack, abbiamo visto come trasferire dati binari da server a client in tempo reale. Lo stesso protocollo può essere usato anche per inviare dati testuali.Il codice presentato in questo script si trova su GitHub, nel seguente repository.
https://github.com/aspitalia/aspnetcore-signalr-binary
Nel ramo start-stop dello stesso repository si trova anche un'implementazione minimale della funzionalità di avvio e arresto che consente ai client di decidere quando ricevere i contenuti binari dal server. In questo modo, l'invio è ottimizzato perché avviene solo quando ci sono client connessi.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Gestire i dati con Azure Cosmos DB Data Explorer
Creare una libreria CSS universale: Clip-path
Change tracking e composition in Entity Framework
Usare i settings di serializzazione/deserializzazione di System.Text.Json di ASP.NET all'interno di un'applicazione non web
Gestire il colore CSS con HWB
Il nuovo controllo Range di Blazor 9
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
Configurare il nome della run di un workflow di GitHub in base al contesto di esecuzione
Ottimizzare le performance usando Span<T> e il metodo Split
Gestire la cancellazione di una richiesta in streaming da Blazor
Utilizzare il trigger SQL con le Azure Function
Creare una libreria CSS universale: i bottoni