клиент .NET core SignalR ASP.NET

Клиентская библиотека .NET для ASP.NET Core SignalR позволяет взаимодействовать с SignalR центрами из приложений .NET.

Просмотреть или скачать образец кода (описание загрузки)

Пример кода в этой статье — это приложение WPF, использующее клиент ASP.NET Core SignalR .NET.

Установка клиентского SignalR пакета .NET

Microsoft.AspNetCore .SignalR. Пакет клиента необходим для подключения клиентов .NET к SignalR концентраторам.

Чтобы установить клиентную библиотеку, выполните следующую команду в окне консоли диспетчер пакетов:

Install-Package Microsoft.AspNetCore.SignalR.Client

Подключение к концентратору

Чтобы установить подключение, создайте HubConnectionBuilder и вызов Build. URL-адрес концентратора, протокол, тип транспорта, уровень журнала, заголовки и другие параметры можно настроить при создании подключения. Настройте все необходимые параметры, вставив в нее HubConnectionBuilder любой из методов Build. Запустите подключение с 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);                
            }
        }
    }
}

Обработка потерянного соединения

Автоматическое повторное подключение

Его HubConnection можно настроить для автоматического повторного подключения с помощью метода в объекте WithAutomaticReconnect HubConnectionBuilder. По умолчанию он не будет автоматически повторно подключаться.

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

Без параметров WithAutomaticReconnect() клиент настраивает ожидание 0, 2, 10 и 30 секунд соответственно, прежде чем пытаться выполнить каждую попытку повторного подключения, остановившись после четырех неудачных попыток.

Перед началом любых попыток HubConnection повторного подключения он перейдет в HubConnectionState.Reconnecting состояние и вызовет Reconnecting событие. Это дает возможность предупредить пользователей о том, что подключение потеряно и отключает элементы пользовательского интерфейса. Неинтерактивные приложения могут начинать очередь или удалять сообщения.

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

Если клиент успешно повторно подключается в течение первых четырех попыток, HubConnection он вернется к состоянию Connected и вызовет Reconnected событие. Это дает возможность сообщить пользователям о том, что подключение было восстановлено и удалено из очереди.

Так как подключение выглядит совершенно новым на сервере, новое ConnectionId будет предоставлено обработчикам Reconnected событий.

Предупреждение

Параметр Reconnected обработчика connectionId событий будет иметь значение NULL, если HubConnection настроено пропускать переговоры.

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() не настраивает повторные HubConnection начальные сбои запуска, поэтому при выполнении сбоев запуска необходимо обрабатывать вручную:

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

Если клиент не успешно пересоединяется в течение первых четырех попыток, HubConnection он перейдет в Disconnected состояние и вызовет Closed событие. Это дает возможность перезапустить подключение вручную или сообщить пользователям, что подключение было потеряно окончательно.

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

Чтобы настроить настраиваемое количество попыток повторного подключения перед отключением или изменением времени повторного подключения, WithAutomaticReconnect принимает массив чисел, представляющий задержку в миллисекундах, чтобы ждать перед началом каждой попытки повторного подключения.

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.

В предыдущем примере настраивается HubConnection запуск повторного подключения сразу после потери подключения. Это также верно для конфигурации по умолчанию.

Если первая попытка повторного подключения завершается сбоем, вторая попытка повторного подключения также начнется немедленно, а не ожидает 2 секунд, как в конфигурации по умолчанию.

Если вторая попытка повторного подключения завершается ошибкой, третья попытка повторного подключения начнется через 10 секунд, которая снова похожа на конфигурацию по умолчанию.

Настраиваемое поведение снова развернется от поведения по умолчанию, остановив после третьего сбоя повторного подключения. В конфигурации по умолчанию будет еще одна попытка повторного подключения в течение еще 30 секунд.

Если требуется еще больше контроля над временем и количеством попыток автоматического повторного подключения, WithAutomaticReconnect принимает объект, реализующий IRetryPolicy интерфейс, имеющий один метод с именем NextRetryDelay.

NextRetryDelay принимает один аргумент с типом RetryContext. Имеет RetryContext три свойства: PreviousRetryCountElapsedTime и RetryReason, которые являются long,a TimeSpan и Exception соответственно. Перед первой попыткой PreviousRetryCount повторного подключения оба и ElapsedTime будет нулевым, и RetryReason будет исключение, которое привело к потере подключения. После каждой неудачной попытки PreviousRetryCount повтора будет увеличиваться по одному, ElapsedTime будет обновлено, чтобы отразить время повторного подключения до сих пор, и RetryReason будет исключение, которое вызвало последнюю попытку повторного подключения.

NextRetryDelay должен возвращать значение TimeSpan, представляющее время ожидания перед следующей попыткой повторного подключения или null HubConnection если необходимо прекратить повторное подключение.

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

Кроме того, можно написать код, который будет повторно подключать клиент вручную, как показано в ручном повторном подключении.

Повторное подключение вручную

Предупреждение

До версии 3.0 клиент .NET для SignalR не выполняет автоматическое повторное подключение. Необходимо написать код, который будет повторно подключать клиент вручную.

Closed Используйте событие для реагирования на потерянное соединение. Например, может потребоваться автоматизировать повторное подключение.

Для Closed события требуется делегат, который возвращает объект Task, который позволяет асинхронный код выполняться без использования async void. Чтобы удовлетворить подпись делегата в Closed обработчике событий, который выполняется синхронно, возвращается Task.CompletedTask:

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

Основная причина асинхронной поддержки заключается в том, что вы можете перезапустить подключение. Запуск соединения — это асинхронное действие.

В обработчике Closed , который перезапускает подключение, рекомендуется ожидать некоторой случайной задержки, чтобы предотвратить перегрузку сервера, как показано в следующем примере:

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

Методы концентратора вызовов из клиента

InvokeAsync вызывает методы в концентраторе. Передайте имя метода концентратора и все аргументы, определенные в методе InvokeAsyncконцентратора. SignalR является асинхронным, поэтому используйте async и await при выполнении вызовов.

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

Метод InvokeAsync возвращает Task значение, которое завершается при возврате метода сервера. Возвращаемое значение, если таковое имеется, предоставляется в качестве результата Task. Все исключения, вызванные методом на сервере, вызывают сбой Task. Используйте await синтаксис, чтобы ждать завершения try...catch и синтаксиса метода сервера для обработки ошибок.

Метод SendAsync возвращает Task значение, которое завершается при отправке сообщения на сервер. Возвращаемое значение не предоставляется, так как это Task не ожидает завершения метода сервера. Все исключения, вызванные клиентом при отправке сообщения, вызывают ошибку Task. Использование await и try...catch синтаксис для обработки ошибок отправки.

Примечание.

Вызов методов концентратора из клиента поддерживается только при использовании службы Azure SignalR в режиме по умолчанию . Дополнительные сведения см. в статье "Часто задаваемые вопросы" (репозиторий Azure-GitHubsignalr).

Вызов клиентских методов из концентратора

Определите методы вызовов концентратора, используя после connection.On сборки, но перед началом подключения.

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

Предыдущий код выполняется при connection.On вызове кода на стороне SendAsync сервера с помощью метода.

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

Примечание.

Хотя центральная сторона подключения поддерживает строго типизированные сообщения, клиент должен зарегистрировать с помощью универсального метода HubConnection.On с именем метода. Пример см. в разделе "Узел ASP.NET Core SignalR " в фоновых службах.

Обработка ошибок и ведение журнала

Обработка ошибок с помощью инструкции try-catch. Exception Проверьте объект, чтобы определить правильное действие после возникновения ошибки.

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

Дополнительные ресурсы