Client .NET di ASP.NET Core SignalR

La libreria client .NET di ASP.NET Core SignalR consente di comunicare con SignalR hub da app .NET.

Visualizzare o scaricare il codice di esempio (procedura per il download)

L'esempio di codice di questo articolo è un'app WPF che usa il client .NET di ASP.NET Core SignalR .

Installare il SignalR pacchetto client .NET

Microsoft.AspNetCore.SignalR. Il pacchetto client è necessario per consentire ai client .NET di connettersi agli SignalR hub.

Per installare la libreria client, eseguire il comando seguente nella finestra della console di Gestione pacchetti:

Install-Package Microsoft.AspNetCore.SignalR.Client

Connettersi a un hub

Per stabilire una connessione, creare un HubConnectionBuilder oggetto e chiamare Build. L'URL dell'hub, il protocollo, il tipo di trasporto, il livello di log, le intestazioni e altre opzioni possono essere configurati durante la compilazione di una connessione. Configurare le opzioni necessarie inserendo uno dei HubConnectionBuilder metodi in Build. Avviare la connessione con StartAsync.

using System;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.AspNetCore.SignalR.Client;

namespace SignalRChatClient
{
    public partial class MainWindow : Window
    {
        HubConnection connection;
        public MainWindow()
        {
            InitializeComponent();

            connection = new HubConnectionBuilder()
                .WithUrl("http://localhost:53353/ChatHub")
                .Build();

            connection.Closed += async (error) =>
            {
                await Task.Delay(new Random().Next(0,5) * 1000);
                await connection.StartAsync();
            };
        }

        private async void connectButton_Click(object sender, RoutedEventArgs e)
        {
            connection.On<string, string>("ReceiveMessage", (user, message) =>
            {
                this.Dispatcher.Invoke(() =>
                {
                   var newMessage = $"{user}: {message}";
                   messagesList.Items.Add(newMessage);
                });
            });

            try
            {
                await connection.StartAsync();
                messagesList.Items.Add("Connection started");
                connectButton.IsEnabled = false;
                sendButton.IsEnabled = true;
            }
            catch (Exception ex)
            {
                messagesList.Items.Add(ex.Message);
            }
        }

        private async void sendButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await connection.InvokeAsync("SendMessage", 
                    userTextBox.Text, messageTextBox.Text);
            }
            catch (Exception ex)
            {                
                messagesList.Items.Add(ex.Message);                
            }
        }
    }
}

Gestire la connessione persa

Riconnettersi automaticamente

L'oggetto HubConnection può essere configurato per riconnettersi automaticamente usando il WithAutomaticReconnect metodo in HubConnectionBuilder. Per impostazione predefinita, la riconnessione non verrà riconnessa automaticamente.

HubConnection connection= new HubConnectionBuilder()
    .WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
    .WithAutomaticReconnect()
    .Build();

Senza parametri, WithAutomaticReconnect() configura il client per attendere rispettivamente 0, 2, 10 e 30 secondi prima di provare ogni tentativo di riconnessione, interrompendo dopo quattro tentativi non riusciti.

Prima di avviare qualsiasi tentativo di riconnessione, passerà HubConnection allo HubConnectionState.Reconnecting stato e genererà l'evento Reconnecting . In questo modo è possibile avvisare gli utenti che la connessione è stata persa e di disabilitare gli elementi dell'interfaccia utente. Le app non interattive possono avviare l'accodamento o eliminare messaggi.

connection.Reconnecting += error =>
{
    Debug.Assert(connection.State == HubConnectionState.Reconnecting);

    // Notify users the connection was lost and the client is reconnecting.
    // Start queuing or dropping messages.

    return Task.CompletedTask;
};

Se il client si riconnette correttamente entro i primi quattro tentativi, l'oggetto HubConnection eseguirà la transizione allo Connected stato e attiverà l'evento Reconnected . In questo modo è possibile informare gli utenti che la connessione è stata ristabilita e annullare la coda di eventuali messaggi in coda.

Poiché la connessione sembra completamente nuova per il server, verrà fornito Reconnected un nuovo ConnectionId gestore eventi.

Avviso

Il Reconnected parametro del connectionId gestore eventi sarà Null se è stato configurato per ignorare la HubConnection negoziazione.

connection.Reconnected += connectionId =>
{
    Debug.Assert(connection.State == HubConnectionState.Connected);

    // Notify users the connection was reestablished.
    // Start dequeuing messages queued while reconnecting if any.

    return Task.CompletedTask;
};

WithAutomaticReconnect() non configurerà per HubConnection ripetere gli errori di avvio iniziale, quindi gli errori di avvio devono essere gestiti manualmente:

public static async Task<bool> ConnectWithRetryAsync(HubConnection connection, CancellationToken token)
{
    // Keep trying to until we can start or the token is canceled.
    while (true)
    {
        try
        {
            await connection.StartAsync(token);
            Debug.Assert(connection.State == HubConnectionState.Connected);
            return true;
        }
        catch when (token.IsCancellationRequested)
        {
            return false;
        }
        catch
        {
            // Failed to connect, trying again in 5000 ms.
            Debug.Assert(connection.State == HubConnectionState.Disconnected);
            await Task.Delay(5000);
        }
    }
}

Se il client non si riconnette correttamente entro i primi quattro tentativi, HubConnection passerà allo Disconnected stato e attiverà l'evento Closed . In questo modo è possibile tentare di riavviare la connessione manualmente o informare gli utenti che la connessione è stata persa definitivamente.

connection.Closed += error =>
{
    Debug.Assert(connection.State == HubConnectionState.Disconnected);

    // Notify users the connection has been closed or manually try to restart the connection.

    return Task.CompletedTask;
};

Per configurare un numero personalizzato di tentativi di riconnessione prima di disconnettersi o modificare l'intervallo di riconnessione, WithAutomaticReconnect accetta una matrice di numeri che rappresenta il ritardo in millisecondi di attesa prima di avviare ogni tentativo di riconnessione.

HubConnection connection= new HubConnectionBuilder()
    .WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
    .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.Zero, TimeSpan.FromSeconds(10) })
    .Build();

    // .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30) }) yields the default behavior.

Nell'esempio precedente viene configurato l'oggetto HubConnection per avviare il tentativo di riconnessione immediatamente dopo la perdita della connessione. Questo vale anche per la configurazione predefinita.

Se il primo tentativo di riconnessione ha esito negativo, il secondo tentativo di riconnessione verrà avviato immediatamente anziché attendere 2 secondi come nella configurazione predefinita.

Se il secondo tentativo di riconnessione ha esito negativo, il terzo tentativo di riconnessione verrà avviato in 10 secondi, come la configurazione predefinita.

Il comportamento personalizzato si differenzia quindi di nuovo dal comportamento predefinito arrestando dopo il terzo tentativo di riconnessione. Nella configurazione predefinita ci sarebbe un altro tentativo di riconnessione in altri 30 secondi.

Se si vuole un maggiore controllo sulla tempistica e sul numero di tentativi di riconnessione automatica, WithAutomaticReconnect accetta un oggetto che implementa l'interfaccia IRetryPolicy , che ha un singolo metodo denominato NextRetryDelay.

NextRetryDelay accetta un singolo argomento con il tipo RetryContext. Ha RetryContext tre proprietà: PreviousRetryCount, ElapsedTime e RetryReason, che sono rispettivamente , longe TimeSpan .Exception Prima del primo tentativo di riconnessione, entrambi PreviousRetryCount e ElapsedTime saranno zero e RetryReason sarà l'eccezione che ha causato la perdita della connessione. Dopo ogni tentativo di ripetizione non riuscito, PreviousRetryCount verrà incrementato di uno, ElapsedTime verrà aggiornato in modo da riflettere la quantità di tempo impiegato per la riconnessione fino a questo momento e RetryReason sarà l'eccezione che ha causato l'ultimo tentativo di riconnessione non riuscito.

NextRetryDelay deve restituire un timeSpan che rappresenta il tempo di attesa prima del tentativo di riconnessione successivo o null se deve interrompere la HubConnection riconnessione.

public class RandomRetryPolicy : IRetryPolicy
{
    private readonly Random _random = new Random();

    public TimeSpan? NextRetryDelay(RetryContext retryContext)
    {
        // If we've been reconnecting for less than 60 seconds so far,
        // wait between 0 and 10 seconds before the next reconnect attempt.
        if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60))
        {
            return TimeSpan.FromSeconds(_random.NextDouble() * 10);
        }
        else
        {
            // If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
            return null;
        }
    }
}
HubConnection connection = new HubConnectionBuilder()
    .WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
    .WithAutomaticReconnect(new RandomRetryPolicy())
    .Build();

In alternativa, è possibile scrivere codice che riconnetterà il client manualmente, come illustrato in Riconnettersi manualmente.

Riconnettersi manualmente

Avviso

Prima della versione 3.0, il client .NET per SignalR non si riconnette automaticamente. È necessario scrivere codice che riconnetterà il client manualmente.

Usare l'evento Closed per rispondere a una connessione persa. Ad esempio, è possibile automatizzare la riconnessione.

L'evento Closed richiede un delegato che restituisce un Taskoggetto , che consente l'esecuzione del codice asincrono senza usare async void. Per soddisfare la firma del delegato in un Closed gestore eventi che viene eseguito in modo sincrono, restituire Task.CompletedTask:

connection.Closed += (error) => {
    // Do your close logic.
    return Task.CompletedTask;
};

Il motivo principale per il supporto asincrono è quindi possibile riavviare la connessione. L'avvio di una connessione è un'azione asincrona.

In un Closed gestore che riavvia la connessione, considerare l'attesa di un ritardo casuale per impedire l'overload del server, come illustrato nell'esempio seguente:

connection.Closed += async (error) =>
{
    await Task.Delay(new Random().Next(0,5) * 1000);
    await connection.StartAsync();
};

Chiamare i metodi dell'hub dal client

InvokeAsync chiama i metodi nell'hub. Passare il nome del metodo hub e tutti gli argomenti definiti nel metodo hub a InvokeAsync. SignalR è asincrono, quindi usare async e await quando si effettuano le chiamate.

await connection.InvokeAsync("SendMessage", 
    userTextBox.Text, messageTextBox.Text);

Il InvokeAsync metodo restituisce un oggetto Task che viene completato quando viene restituito il metodo server. Il valore restituito, se presente, viene fornito come risultato di Task. Tutte le eccezioni generate dal metodo nel server generano un errore Task. Usare await la sintassi per attendere il completamento e try...catch la sintassi del metodo server per gestire gli errori.

Il SendAsync metodo restituisce un oggetto Task che viene completato quando il messaggio è stato inviato al server. Non viene fornito alcun valore restituito perché Task non attende il completamento del metodo server. Tutte le eccezioni generate nel client durante l'invio del messaggio generano un errore Task. Usare await e try...catch sintassi per gestire gli errori di invio.

Nota

La chiamata dei metodi hub da un client è supportata solo quando si usa il servizio di Azure SignalR in modalità predefinita . Per altre informazioni, vedere Domande frequenti (azure-signalr repository GitHub).

Chiamare i metodi client dall'hub

Definire i metodi che l'hub chiama usando connection.On dopo la compilazione, ma prima di avviare la connessione.

connection.On<string, string>("ReceiveMessage", (user, message) =>
{
    this.Dispatcher.Invoke(() =>
    {
       var newMessage = $"{user}: {message}";
       messagesList.Items.Add(newMessage);
    });
});

Il codice precedente in connection.On viene eseguito quando il codice sul lato server lo chiama usando il SendAsync metodo .

public async Task SendMessage(string user, string message)
{
    await Clients.All.SendAsync("ReceiveMessage", user,message);
}

Nota

Mentre il lato hub della connessione supporta la messaggistica fortemente tipizzata, il client deve eseguire la registrazione usando il metodo HubConnection.On generico con il nome del metodo. Per un esempio, vedere Host ASP.NET Core SignalR nei servizi in background.

Registrazione e gestione degli errori

Gestire gli errori con un'istruzione try-catch. Esaminare l'oggetto Exception per determinare l'azione appropriata da eseguire dopo un errore.

try
{
    await connection.InvokeAsync("SendMessage", 
        userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{                
    messagesList.Items.Add(ex.Message);                
}

Risorse aggiuntive