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 , long
e 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 Task
oggetto , 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 ulteriori informazioni, vedere Domande frequenti.
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);
}