Что такое SignalR и зачем он нужен?
SignalR — это библиотека для ASP.NET Core, которая упрощает добавление функций реального времени (real-time) в веб-приложения. Она позволяет серверу мгновенно отправлять данные подключенным клиентам (браузерам, мобильным приложениям, десктопным приложениям) без необходимости постоянных запросов от клиента.
Зачем это нужно? Традиционный HTTP-протокол работает по принципу «запрос-ответ»: клиент отправляет запрос, сервер отвечает. Для получения обновлений клиент должен постоянно опрашивать сервер (polling). Это неэффективно и создает задержки. SignalR решает эту проблему, предоставляя постоянное двустороннее соединение.
Основные сценарии использования:
- Чаты и мессенджеры (мгновенная доставка сообщений)
- Панели мониторинга и дашборды (обновление курсов валют, котировок акций)
- Совместное редактирование документов (Google Docs-like)
- Уведомления в реальном времени (новые заказы, оповещения)
- Многопользовательские игры
- Прогресс выполнения длительных операций (загрузка файлов, обработка данных)
SignalR автоматически выбирает наилучший транспортный протокол для соединения:
- WebSocket — самый эффективный, постоянное двустороннее соединение
- Server-Sent Events (SSE) — односторонняя передача от сервера к клиенту
- Long Polling — fallback-механизм, если WebSocket и SSE недоступны
Установка SignalR
Установка на сервер (ASP.NET Core)
Для серверной части используется NuGet-пакет. Выполните в терминале:
dotnet add package Microsoft.AspNetCore.SignalR
Или через Package Manager Console в Visual Studio:
Install-Package Microsoft.AspNetCore.SignalR
Установка клиента для JavaScript (браузер)
Для клиентской части (браузер) используйте npm:
npm install @microsoft/signalr
Или подключите через CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js"></script>
Установка клиента для .NET (C#)
Для клиента на C# (например, консольное приложение или WPF):
dotnet add package Microsoft.AspNetCore.SignalR.Client
Быстрый старт: пример "Hello, World!"
Создадим простой чат: сервер принимает сообщение и рассылает его всем подключенным клиентам.
Шаг 1: Создаем Hub (серверная часть)
Создайте файл ChatHub.cs:
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Hubs
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
// Отправляем сообщение всем подключенным клиентам
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
Шаг 2: Регистрируем SignalR в Program.cs
using SignalRChat.Hubs;
var builder = WebApplication.CreateBuilder(args);
// Добавляем SignalR
builder.Services.AddSignalR();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
// Маппим хаб на путь /chatHub
app.MapHub<ChatHub>("/chatHub");
app.Run();
Шаг 3: Создаем клиент (HTML + JavaScript)
<!DOCTYPE html>
<html>
<head>
<title>SignalR Chat</title>
</head>
<body>
<h2>SignalR Chat</h2>
<div>
<input type="text" id="userInput" placeholder="Ваше имя" />
<input type="text" id="messageInput" placeholder="Сообщение" />
<button onclick="sendMessage()">Отправить</button>
</div>
<ul id="messagesList"></ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js"></script>
<script>
// Создаем соединение
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build();
// Подписываемся на получение сообщений
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messagesList").appendChild(li);
});
// Запускаем соединение
connection.start().catch(err => console.error(err.toString()));
// Функция отправки сообщения
function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(err => console.error(err.toString()));
}
</script>
</body>
</html>
Основные методы/функции SignalR
1. HubConnectionBuilder.WithUrl() (клиент)
Что делает: Настраивает URL-адрес хаба для подключения.
Параметры:
url(string) — URL хаба на сервереoptions(Action<HttpConnectionOptions>) — дополнительные опции (токены, заголовки)
Пример:
var connection = new HubConnectionBuilder()
.WithUrl("https://myserver.com/chatHub")
.Build();
2. HubConnection.On() (клиент)
Что делает: Подписывается на получение сообщений от сервера с указанным именем метода.
Параметры:
methodName(string) — имя метода на сервереhandler(Delegate) — функция-обработчик
Пример:
connection.On<string, string>("ReceiveMessage", (user, message) => {
Console.WriteLine($"{user}: {message}");
});
3. HubConnection.InvokeAsync() (клиент)
Что делает: Вызывает метод на сервере и ожидает результат (если метод возвращает значение).
Параметры:
methodName(string) — имя вызываемого методаargs(object[]) — аргументы метода
Пример:
await connection.InvokeAsync("SendMessage", "User1", "Hello!");
4. HubConnection.SendAsync() (клиент)
Что делает: Вызывает метод на сервере без ожидания результата (fire-and-forget).
Параметры: Аналогично InvokeAsync.
Пример:
await connection.SendAsync("SendMessage", "User1", "Hello!");
5. Clients.All.SendAsync() (сервер)
Что делает: Отправляет сообщение всем подключенным клиентам.
Параметры:
method(string) — имя метода на клиентеargs(object[]) — аргументы
Пример:
await Clients.All.SendAsync("ReceiveMessage", user, message);
6. Clients.Caller.SendAsync() (сервер)
Что делает: Отправляет сообщение только тому клиенту, который вызвал текущий метод.
Пример:
await Clients.Caller.SendAsync("ReceiveMessage", "Сервер", "Привет, только тебе!");
7. Clients.Others.SendAsync() (сервер)
Что делает: Отправляет сообщение всем клиентам, кроме вызвавшего.
Пример:
await Clients.Others.SendAsync("ReceiveMessage", user, message);
8. Clients.Group().SendAsync() (сервер)
Что делает: Отправляет сообщение всем клиентам в указанной группе.
Параметры:
groupName(string) — имя группы
Пример:
await Clients.Group("Room1").SendAsync("ReceiveMessage", user, message);
9. Groups.AddToGroupAsync() (сервер)
Что делает: Добавляет текущее соединение в указанную группу.
Параметры:
connectionId(string) — ID соединенияgroupName(string) — имя группы
Пример:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "Room1");
await base.OnConnectedAsync();
}
10. Groups.RemoveFromGroupAsync() (сервер)
Что делает: Удаляет текущее соединение из указанной группы.
Пример:
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "Room1");
11. Hub.OnDisconnectedAsync() (сервер)
Что делает: Вызывается при отключении клиента. Можно переопределить для очистки ресурсов.
Пример:
public override async Task OnDisconnectedAsync(Exception? exception)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "Room1");
await base.OnDisconnectedAsync(exception);
}
12. HubConnection.StopAsync() (клиент)
Что делает: Останавливает соединение с сервером.
Пример:
await connection.StopAsync();
Реальный практический пример: Система уведомлений
Создадим систему уведомлений для интернет-магазина: при поступлении нового заказа администратор получает мгновенное оповещение.
Серверная часть (NotificationHub.cs)
using Microsoft.AspNetCore.SignalR;
namespace StoreApp.Hubs
{
public class NotificationHub : Hub
{
// Администраторы подключаются к группе "Admins"
public async Task JoinAdminGroup()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "Admins");
}
// Метод для отправки уведомления всем администраторам
public async Task NotifyNewOrder(string orderId, string customerName, decimal amount)
{
await Clients.Group("Admins").SendAsync("NewOrderNotification", orderId, customerName, amount);
}
}
}
Контроллер для создания заказа (OrdersController.cs)
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using StoreApp.Hubs;
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IHubContext<NotificationHub> _hubContext;
public OrdersController(IHubContext<NotificationHub> hubContext)
{
_hubContext = hubContext;
}
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] OrderDto order)
{
// Логика создания заказа в БД...
// Отправляем уведомление администратора