Клиент на .NET

Последнее обновление: 08.08.2022

наряду с клиентом для javascript библиотека SignalR также поддерживает создание клиентских приложений для .NET. Рассмотрим основные моменты клиента для .NET на примере простейшего приложения на WPF.

Создание сервера

Сначала определим код сервера, с которым будет взаимодействовать клиент на .NET MAUI. Для этого создадим проект ASP.NET Core по типу Empty.

Определим в проекте следующий простейший класс хаба:

using Microsoft.AspNetCore.SignalR;

namespace SignalRApp
{
    public class ChatHub : Hub
    {
        public async Task Send(string username, string message)
        {
            await this.Clients.All.SendAsync("Receive", username, message);
        }
    }
}

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

В файле Program.cs определим следующий код:

using SignalRApp;   // пространство имен класса ChatHub

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSignalR();

var app = builder.Build();


app.UseDefaultFiles();
app.UseStaticFiles();

app.MapHub<ChatHub>("/chat");
app.Run();

Создание клиента .NET MAUI

Прежде всего, создадим новый проект по типу WPF Application:

Создание проекта WPF для SignalR на C#

Допустим, проект будет называться SignalrWpfClient.

После создания проекта в первую очередь добавим в него Nuget-пакет Microsoft.AspNetCore.SignalR.Client.

Добавление пакета Microsoft.AspNetCore.SignalR.Client в проект WPF для создания клиента на .NET и C#

По умолчанию проект WPF содержит определение главного окна в файлах MainWindow.xaml и MainWindow.xaml.cs. В файле MainWindow.xaml определим следующий интерфейс окна:

<Window x:Class="SignalrWpfClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="METANIT.COM" Height="450" Width="300" Loaded="Window_Loaded">
    <StackPanel Margin="4">
        <Label Content="Введите логин"/>
        <TextBox x:Name="userTextBox" />
        <Label Content="Введите сообщение"/>
        <TextBox x:Name="messageTextBox" />
        <Button x:Name="sendBtn" Content="Отправить" Click="Button_Click" IsEnabled="False" />
        <ListBox x:Name="chatbox" />
    </StackPanel>
</Window>

Здесь определены два текстовых поля для ввода имени пользователя и сообщения. Также здесь определена кнопка, по нажатию на которую введенные в текстовые поля данные будут отправляться хабу.

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

Теперь изменим код в файле MainWindow.xaml.cs:

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

namespace SignalrWpfClient
{
    public partial class MainWindow : Window
    {
        HubConnection connection;  // подключение для взаимодействия с хабом
        public MainWindow()
        {
            InitializeComponent();

            // создаем подключение к хабу
            connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:7098/chat")
                .Build();

            
            // регистрируем функцию Receive для получения данных
            connection.On<string, string>("Receive", (user, message) =>
            {
                Dispatcher.Invoke(() =>
                {
                    var newMessage = $"{user}: {message}";
                    chatbox.Items.Insert(0, newMessage);
                });
            });
        }
        
        // обработчик загрузки окна
        private async void Window_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                // подключемся к хабу
                await connection.StartAsync();
                chatbox.Items.Add("Вы вошли в чат");
                sendBtn.IsEnabled = true;
            }
            catch (Exception ex)
            {
                chatbox.Items.Add(ex.Message);
            }
        }
        // обработчик нажатия на кнопку
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // отправка сообщения
                await connection.InvokeAsync("Send", userTextBox.Text, messageTextBox.Text);
            }
            catch (Exception ex)
            {
                chatbox.Items.Add(ex.Message);
            }
        }
    }
}

В данном случае у меня сервер запущен по адресу "https://localhost:7098/", поэтому я указываю этот адрес для подключения к хабу:

connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:7098/chat")

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

Клиентское приложение на WPF для приложения SignalR на ASP.NET Core и C#

Теперь рассмотрим подробнее основные моменты работы клиента Signalr для .NET.

Создание подключения

Чтобы подключиться к хабу, применяется объект HubConnection:

HubConnection connection;

Для создания данного объекта применяется специальный класс-строитель HubConnectionBuilder:

connection = new HubConnectionBuilder()
        .WithUrl("https://localhost:7098/chat")
        .Build();

Через его метод WithUrl передается адрес, по которому доступен хаб. А метод Build() собственно создает объект подключения - объект HubConnection, через который мы можем взаимодействовать с хабом.

Получение данных от хаба

Далее регистрируем метод, которая будет получать данные от хаба с помощью метода connection.On()

connection.On<string, string>("Receive", (user, message) =>
{
    Dispatcher.Invoke(() =>
    {
        var newMessage = $"{user}: {message}";
        chatbox.Items.Insert(0, newMessage);
    });
});

Первый параметр метода connection.On() представляет имя метода, который будет получать данные от хаба, а второй параметр - собственно определение этого метода.

Например, выше в методе Send в хабе полученные данные транслировались на метод Receive всем подключенным клиентам:

await Clients.All.SendAsync("Receive", username, message);

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

Поэтому первый параметр метода connection.On представляет строка "Receive", а второй параметр - метод или точнее лямбда-выражение получает отправленные хабом данные в виде параметров. А поскольку хаб посылает две строки, то метод connection.On() типизируется двумя типами string.

В данном случае при получении данных от хаба с помощью метода Dispatcher.Invoke мы можем обратиться к пользовательскому интерфейсу и добавить полученные данные в список chatbox, который представляет элемент ListBox.

Подключение к хабу

После получения объекта HubConnection для подключения к хабу надо вызвать метод StartAsync(). В пример выше это делается в обработчике события загрузки окна:

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    try
    {
        // подключемся к хабу
        await connection.StartAsync();
        chatbox.Items.Add("Вы вошли в чат");
        sendBtn.IsEnabled = true;
    }
    catch (Exception ex)
    {
        chatbox.Items.Add(ex.Message);
    }
}

В случае успешного выполнения метода StartAsync пользователь может начать взаимодействовать с хабом.

Отправка данных хабу

Для отправки данных хабу у объекта HubConnection применяется метод InvokeAsync(). Например, выше в классе хаба ChatHub был определен метод Send, который принимает два параметра. В клиенте на WPF отправка этому методу происходит в обработчике нажатия кнопки:

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

Первый параметр метода представляет имя метода хаба, к которому идет обращения. Например, в данном случае это метод Send. Все последующие параметры передают данные для параметров метода хаба. Так, метод Send в хабе ChatHub принимает два строковых параметра. Соответственно в методе InvokeAsync мы можем передать для них данные.

Параметры передаются по позиции: второй аргумент метода InvokeAsync передает значение для первого параметра метода Send, третий аргумент в InvokeAsync - для второго параметра в Send и так далее.

Отключение от хаба

При необходимости после подключения к хабу мы можем отключиться от него с помощью метода StopAsync() объекта HubConnection. Например, можно было бы определить для события Closing - события, которое возникает перед закрытием окна, следующий обработчик:

private async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    await connection.InvokeAsync("Send", "",$"Пользователь {userTextBox.Text} выходит из чата");
    await connection.StopAsync();   // отключение от хаба
}

Свойства и события HubConnection

HubConnection имеет ряд свойств, которые позволяют получить информацию о подключении или сконфигурировать клиент:

  • ConnectionId представляет индификатор текущего подключения

  • ServerTimeout представляет таймаут, в течение которого подключение считается активным. Если в течение этого периода сервер не присылает никакого сообщения, то клиент считает, что подключение к серверу разорвано. И в этом случае вызывается событие Closed().

  • KeepAliveInterval определяет интервал, в течение которого клиент посылает пинг-сообщения серверу. Отправка любого сообщения от клиента сбрасывает таймер для отслеживания этого интервала до нуля. Если клиент не отправит никакого сообщения в течении интервала, который устанавливается свойством ClientTimeoutInterval класса хаба на сервере, то сервер считает, что клиент отключился

События HubConnection:

  • Closed возникает после закрытия подключения

  • Reconnected возникает после переподключения к хабу.

  • Reconnecting возникает перед переподключением к хабу

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

Возможна ситуация, что соединение с хабом будет потеряно. Если мы хотим, чтобы подключение было восстановлено, то мы можем сделать это автоматически, используя у HubConnectionBuilder метод WithAutomaticReconnect():

connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:7098/chat")
                .WithAutomaticReconnect()   // автопереподключение
                .Build();

При необходимости можно указать временные интервалы для переподключения с помощью массива TimeSpan:

connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:7098/chat")
                .WithAutomaticReconnect(new[] { TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(20) })
                .Build();

В данном случае клиент пытается переподключить сначала через 10, а потом через 20 секунд. Можно задать произвольное количество подобных интервалов.

Перед переподключением возникает событие Reconnecting, а после переподключения - событие Reconnected. Соответственно, если небходимо отследить переподключение, то можно обработать данные события:

connection.Reconnecting += error =>
{
    // обработка события

    return Task.CompletedTask;
};
connection.Reconnected += connectionId =>
{
    // обработка события

    return Task.CompletedTask;
};

В обработчик события Reconnecting передается информация об ошибке в виде объекта Exception, а в обработчик события Reconnected передается новый идентификатор подключения.

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

connection.Closed += async (error) =>
{
    await Task.Delay(1000); // черех секунду переподключаемся
    await connection.StartAsync();
};

Логгирование

У объекта HubConnectionBuilder есть метод ConfigureLogging(), в который передается делегат Action<Microsoft.Extensions.Logging.ILoggingBuilder>. Объект ILoggingBuilder позволяет настроить ряд параметров логгирования, в частности, с помощью метода SetMinimumLevel() устанавливается уровень логгирования:

connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:7098/chat")
                .ConfigureLogging(logging => 
                {
                    logging.SetMinimumLevel(LogLevel.Information);
                })
                .Build();

Все возможные уровни логгирования:

  • LogLevel.None: логгирование отключено

  • LogLevel.Critical: логгирование сообщений об ошибках, которые относятся ко всему приложению в целом

  • LogLevel.Error: логгирование сообшений об ошибках, которые относятся к текущей операции

  • LogLevel.Warning: логгирование сообщений, которые не представляют ошибки

  • LogLevel.Information: логгирование информационных сообщений

  • LogLevel.Debug: логгирование диагностических сообщений, используемых при отладке

  • LogLevel.Trace: логгирование диагностических сообщений с детальной информацией

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850