ASP.NET Core SignalR .NET-Client

Mithilfe der ASP.NET Core SignalR .NET-Clientbibliothek können Sie in .NET-Apps mit SignalR-Hubs kommunizieren.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

Das Codebeispiel in diesem Artikel ist eine WPF-App, die den ASP.NET Core SignalR .NET-Client verwendet.

Installieren des SignalR .NET-Clientpakets

Das Paket Microsoft.AspNetCore.SignalR.Client wird für .NET-Clients zum Herstellen einer Verbindung mit SignalR-Hubs benötigt.

Führen Sie zum Installieren der Clientbibliothek im Fenster Paket-Manager-Konsole den folgenden Befehl aus:

Install-Package Microsoft.AspNetCore.SignalR.Client

Herstellen einer Verbindung mit einem Hub

Erstellen Sie zum Herstellen einer Verbindung einen HubConnectionBuilder, und rufen Sie Build auf. Hub-URL, Protokoll, Transporttyp, Protokollebene, Header und andere Optionen können beim Aufbauen einer Verbindung konfiguriert werden. Konfigurieren Sie alle erforderlichen Optionen, indem Sie eine der HubConnectionBuilder-Methoden in Build einfügen. Starten Sie die Verbindung mit 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);                
            }
        }
    }
}

Behandeln unterbrochener Verbindungen

Automatisches Wiederverbinden

HubConnection kann so konfiguriert werden, dass die Verbindung mithilfe der WithAutomaticReconnect-Methode für HubConnectionBuilder automatisch wiederhergestellt wird. Standardmäßig wird die Verbindung nicht automatisch wiederhergestellt.

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

Ohne Parameter wird der Client durch WithAutomaticReconnect() so konfiguriert, dass er 0, 2, 10 bzw. 30 Sekunden wartet, ehe er einen Widerverbindungsversuch unternimmt, und nach vier fehlgeschlagenen Versuchen beendet wird.

Ehe ein Wiederverbindungsversuch gestartet wird, geht HubConnection in den Zustand HubConnectionState.Reconnecting über und löst das Ereignis Reconnecting aus. Dies bietet die Möglichkeit, Benutzer zu warnen, dass die Verbindung unterbrochen wurde, und Benutzeroberflächenelemente zu deaktivieren. Nicht interaktive Apps können Nachrichten in die Warteschlange stellen oder löschen.

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;
};

Wenn der Client die Verbindung bei den ersten vier Versuchen erfolgreich wiederherstellt, geht HubConnection wieder in den Zustand Connected über und löst das Ereignis Reconnected aus. Dies bietet die Möglichkeit, Benutzern mitzuteilen, dass die Verbindung wiederhergestellt wurde, und alle Nachrichten aus der Warteschlange zu entfernen.

Da die Verbindung für den Server völlig neu aussieht, wird für die Reconnected-Ereignishandler ein neues ConnectionId-Ereignis bereitgestellt.

Warnung

Der connectionId-Parameter des Reconnected-Ereignishandlers ist NULL, wenn der HubConnection-Ereignishandler so konfiguriert wurde, dass die Aushandlung übersprungen wird.

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;
};

Durch WithAutomaticReconnect() wird HubConnection nicht für eine Wiederholung nach anfänglichen Startfehlern konfiguriert, weshalb Startfehler manuell behandelt werden müssen:

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);
        }
    }
}

Wenn der Client die Verbindung bei den ersten vier Versuchen nicht erfolgreich wiederherstellt, geht HubConnection in den Zustand Disconnected über und löst das Ereignis Closed aus. Dadurch haben Sie die Möglichkeit, zu versuchen, die Verbindung manuell neu zu starten oder Benutzer zu informieren, dass die Verbindung dauerhaft unterbrochen wurde.

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;
};

Um eine benutzerdefinierte Anzahl von Wiederverbindungsversuchen zu konfigurieren, bevor die Verbindung getrennt wird, oder um den Zeitpunkt der Wiederverbindung zu ändern, akzeptiert WithAutomaticReconnect ein Array von Zahlen, die die Verzögerung in Millisekunden angeben, die gewartet werden soll, ehe die einzelnen Wiederverbindungsversuche gestartet werden.

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.

Im vorhergehenden Beispiel wird HubConnection so konfiguriert, dass sofort nach Unterbrechung der Verbindung ein neuer Verbindungsversuch unternommen wird. Dies gilt auch für die Standardkonfiguration.

Wenn der erste Wiederverbindungsversuch fehlschlägt, wird der zweite Wiederverbindungsversuch ebenfalls sofort gestartet, anstatt wie in der Standardkonfiguration zwei Sekunden zu warten.

Wenn der zweite Wiederverbindungsversuch fehlschlägt, wird der dritte Wiederverbindungsversuch nach 10 Sekunden gestartet, was wiederum der Standardkonfiguration entspricht.

Das benutzerdefinierte Verhalten weicht dann erneut vom Standardverhalten ab, da nach dem dritten fehlgeschlagenen Wiederverbindungsversuch der Vorgang abgebrochen wird. Die Standardkonfiguration sieht einen weiteren Wiederverbindungsversuch nach weiteren 30 Sekunden vor.

Wenn Sie noch mehr Kontrolle über Zeitpunkt und Anzahl der automatischen Wiederverbindungsversuche wünschen, akzeptiert WithAutomaticReconnect ein Objekt, das die IRetryPolicy-Schnittstelle implementiert, die eine einzige Methode namens NextRetryDelay hat.

NextRetryDelay akzeptiert ein einzelnes Argument mit dem Typ RetryContext. RetryContext hat drei Eigenschaften: PreviousRetryCount, ElapsedTime und RetryReason, die jeweils long, TimeSpan und Exception bedeuten. Vor dem ersten Wiederverbindungsversuch sind sowohl PreviousRetryCount als auch ElapsedTime 0, und RetryReason ist die Ausnahme, die den Verbindungsabbruch verursacht hat. Nach jedem fehlgeschlagenen Wiederverbindungsversuch wird PreviousRetryCount um 1 erhöht. ElapsedTime wird aktualisiert, um die bisher für den Wiederverbindungsversuch aufgewendete Zeit wiederzugeben, und RetryReason ist die Ausnahme, die den letzten Wiederverbindungsversuch fehlschlagen ließ.

NextRetryDelay muss entweder einen TimeSpan-Wert mit der Zeit zurückgeben, die bis zum nächsten Wiederverbindungsversuch gewartet werden muss, oder null, wenn HubConnection den Wiederverbindungsversuch abbrechen soll.

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();

Alternativ können Sie Code schreiben, der die Verbindung mit Ihrem Client manuell wiederherstellt, wie unter Manuelles Wiederverbinden gezeigt.

Manuelles Wiederherstellen der Verbindung

Warnung

Vor Version 3.0 stellt der .NET-Client für SignalR die Verbindung nicht automatisch wieder her. Sie müssen Code schreiben, mit dem die Verbindung mit Ihrem Client manuell wiedergeherstellt wird.

Verwenden Sie das Closed-Ereignis, um auf eine unterbrochene Verbindung zu reagieren. Angenommen, Sie möchten das Wiederverbinden automatisieren.

Das Closed-Ereignis erfordert einen Delegaten, der Task zurückgibt, wodurch asynchroner Code ohne Verwendung von async void ausgeführt werden kann. Um die Delegatsignatur in einem Closed-Ereignishandler zu erfüllen, der synchron ausgeführt wird, geben Sie Task.CompletedTask zurück:

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

Der Hauptgrund für die asynchrone Unterstützung ist, dass Sie die Verbindung neu starten können. Das Starten einer Verbindung ist eine asynchrone Aktion.

In einem Closed-Handler, der die Verbindung neu startet, sollten Sie eine zufällige Verzögerung abwarten, um eine Überlastung des Servers zu vermeiden, wie im folgenden Beispiel gezeigt:

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

Aufrufen von Hubmethoden auf dem Client

InvokeAsync ruft Methoden auf dem Hub auf. Übergeben Sie den Namen der Hubmethode und in der Hubmethode definierte Argumente an InvokeAsync. SignalR ist asynchron, weshalb Sie für die Aufrufe async und await verwenden sollten.

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

Die InvokeAsync-Methode gibt einen Task zurück, der abgeschlossen wird, wenn die Servermethode zurückgegeben wird. Der Rückgabewert, falls vorhanden, wird als Ergebnis von Task angegeben. Alle Ausnahmen, die von der Methode auf dem Server ausgelöst werden, führen zu einem fehlgeschlagenen Task. Verwenden Sie die await-Syntax, um auf den Abschluss der Servermethode zu warten, und die try...catch-Syntax, um Fehler zu behandeln.

Die SendAsync-Methode gibt einen Task zurück, der abgeschlossen ist, wenn die Nachricht an den Server gesendet wurde. Es wird kein Rückgabewert bereitgestellt, da diese Task-Methode nicht wartet, bis die Servermethode abgeschlossen ist. Alle Ausnahmen, die auf dem Client beim Senden der Nachricht ausgelöst werden, führen zu einem fehlgeschlagenen Task. Verwenden Sie await- und try...catch-Syntax, um Sendefehler zu behandeln.

Hinweis

Das Aufrufen von Hubmethoden durch einen Client wird nur bei Verwendung von Azure SignalR Service im Modus Default unterstützt. Weitere Informationen finden Sie unter Häufig gestellte Fragen (GitHub-Repository azure-signalr).

Aufrufen von Clientmethoden auf dem Hub

Definieren Sie Methoden, die der Hub mithilfe von connection.On nach dem Aufbauen, aber vor dem Starten der Verbindung aufruft.

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

Der vorherige Code in connection.On wird ausgeführt, wenn serverseitiger Code ihn mit der SendAsync-Methode aufruft.

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

Hinweis

Während die Hubseite der Verbindung stark typisiertes Messaging unterstützt, muss sich der Client über die generische HubConnection.On-Methode mit dem Methodennamen registrieren. Ein Beispiel finden Sie unter Hosten von ASP.NET Core SignalR in Hintergrunddiensten.

Fehlerbehandlung und Protokollierung

Behandeln Sie Fehler mit einer Try-Catch-Anweisung. Überprüfen Sie das Exception-Objekt, um die gewünscht Aktion zu ermitteln, die nach Auftreten eines Fehlers erfolgen soll.

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

Zusätzliche Ressourcen