Что такое signalr
Перейти к содержимому

Что такое signalr

  • автор:

Основы SignalR

SignalR Core представляет библиотеку от компании Microsoft, которая работает поверх ASP.NET и которая предназначена для создания приложений, работающих в режиме реального времени. В частности, ее можно использовать вместе с ASP.NET Core. SignalR использует двунаправленную связь для обмена сообщениями между клиентом и сервером, благодаря чему сервер может отправлять в режиме реального времени всем клиентам некоторые данные, а клиенты также в режиме реального времени могут передавать данные серверу и другим клиентам.

Где может использоваться SignalR? Прежде всего это приложения, которые получают данные в реальном режиме времени, например, чаты, социальные сети, игровые приложения, карты, приложения для аукционов, голосований и карт, панели управления, приложения для мониторинга данных и так далее.

Для обмена сообщениями между клиентом и сервером SignalR использует ряд механизмов:

  • WebSockets
  • Server-Side Events
  • Long Polling

Исходя из возможностей клиента и сервера инфраструктура SignalR выбирает наилучший механизм для взаимодействия. В частности, наиболее оптимальным является WebSockets, соответственно если и клиент, и сервер позволяют использовать этот механизм, то взаимодействие идет через WebSockets. Однако если WebSockets не поддерживается, то применяется Server-Side Events. И если SSE не поддерживается, то применяется Long Polling.

Поддерживаемые клиенты

SignalR обеспечивает взаимодействие клиента с сервером. Если на стороне сервера ожидаемое это приложение ASP.NET Core, то на стороне клиента все намного интереснее. В частности, в качестве клиента в SignalR может выступать:

  • Приложение на JavaScript, запущенное на Node.js (поддерживается версия Node.js 8 и выше)
  • Приложение на JavaScript, которое работает в рамках браузеров Google Chrome (в том числе на Android), Microsoft Edge, Mozilla Firefox, Opera, Apple Safari (MacOS/iOS)
  • Приложение на .NET. Это может быть десктопное приложение WPF, Windows Forms, мобильное приложение .NET MAUI.
  • Приложение на языке Java
  • Есть экспериментальная поддержка для приложений на языках C++ и Swift

Первое приложение

Создадим новый простейший проект ASP.NET Core. Если мы работаем в Visual Studio, то надо создать проект по типу ASP.NET Core Empty :

Первый проект на SignalR в ASP.NET Core и C#

Если мы работаем в текстовом редаторе и используем .NET CLI, то надо создать проект по шаблону web :

dotnet new web

Определение серверной части

При работы с SignalR на стороне сервера необходимо создать специальную сущность — хаб (hub). По сути хаб представляет класс, наследующийся от класса Hub, который может обрабатывать запросы. Создадим новый хаб. Для этого добавим в проект следующий класс ChatHub :

using Microsoft.AspNetCore.SignalR; namespace SignalRApp < public class ChatHub : Hub < public async Task Send(string message) < await this.Clients.All.SendAsync("Receive", message); >> >

Класс хаба наследуется от класса Hub . И здесь определен один метод Send() , который получает некоторое отправленное сообщение в виде параметра message и затем с помощью вызова await Clients.All.SendAsync(«Send», message) ретранслирует это сообщение всем подключенным клиентам.

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

Конфигурация приложения

Но чтобы SignalR и хаб ChatHub заработали, необходимо подключить необходимые службы и настроить маршруты в приложении. Для этого откроем файл Program.cs и изменим его код следующим образом:

using SignalRApp; // пространство имен класса ChatHub var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR(); // подключема сервисы SignalR var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles(); app.MapHub("/chat"); // ChatHub будет обрабатывать запросы по пути /chat app.Run();

Прежде всего чтобы добавить в приложение сервисы SignalR, вызывается метод:

services.AddSignalR();

Затем устанавливаем маршруты для хаба ChatHub:

app.MapHub("/chat");

У объекта IEndpointRouteBuilder вызывается метод MapHub , который позволяет связать запросы и класс хаба. В данном случае он устанавливает класс ChatHub в качестве обработчика запросов по пути «/chat». То есть, чтобы обратиться к хабу, строка запроса должна иметь вид типа «https://localhost:5000/chat».

Стоит отметить, что если адрес сервера и адрес клиента не будут совпадать, то, возможно, потребуется настроить поддержку CORS. В данном же случае серверная и клиентская части будут работать в рамках одного приложения, поэтому настройки CORS не потребуется.

Создание клиентской части

Для создания клиентской части можно использовать различные способы. Например, можно использовать javascript, либо же использовать typescript, определить приложение на .NET или Java. В данном случае мы будем использовать JavaScript, который будет выполняться на обычной странице html.

Для хранения статических файлов добавим в проект папку wwwroot . Затем добавим в папку wwwroot новый файл index.html . В итоге проект будет выглядеть следующим образом:

Первый проект signalr в Asp.Net Core и C#

На странице index.html определим следующий код:

    Metanit.com   

На странице определено текстовое поле для ввода сообщение и кнопка для его отправки. Под ними расположен блок chatroom, в который будут добавляться полученные сообщения.

Внизу страницы подключается скрипт «signalr.min.js». Далее в коде javascript определена основная логика взаимодействия клиента с хабом.

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

const hubConnection = new signalR.HubConnectionBuilder() .withUrl("/chat") .build();

Для взаимодействия с хабом ChatHub с помощью метода build() объекта HubConnectionBuilder создается объект hubConnection — объект подключения. Метод withUrl устанавливает адрес, по которому приложение будет обращаться к хабу. Поскольку ChatHub на сервере сопоставляется с адресом «/chat», то именно этот адрес и передается в withUrl.

Далее устанавливается обработчик для кнопки, который вызывается при ее нажатии:

document.getElementById("sendBtn").addEventListener("click", function () < let message = document.getElementById("message").value; hubConnection.invoke("Send", message) .catch(function (err) < return console.error(err.toString()); >); >);

Для отправки данных хабу на сервер вызывается метод hubConnection.invoke(«Send», message) , первый параметр которого представляет метод хаба, обрабатывающий данный запрос, а второй параметр — данные, отправляемые на сервер.

В случае если при отправке возникнет ошибка, сработает функция, которая передается в метод catch()

Далее метод hubConnection.on устанавливает метод на стороне клиента, который будет получать данные от сервера:

hubConnection.on("Receive", function(message) < let messageElement = document.createElement("p"); messageElement.textContent = message; document.getElementById("chatroom").appendChild(messageElement); >);

В данном случае метод называется Receive и фактически он представляют функцию, которая передается в качестве второго параметра. Эта функция принимает один параметр — message — те данные, которые в хабе отправляются клиенту. В самой функции с помощью стандартных функций javascript создается элемент. В этот элемент помещается присланное с сервера сообщение. Затем элемент добавляется в начало элемента chatroom.

И для начала соединения с сервером вызывается функция hubConnection.start() .

hubConnection.start() .then(function () < document.getElementById("sendBtn").disabled = false; >) .catch(function (err) < return console.error(err.toString()); >);

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

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

Взаимодействие между клиентом и сервером в приложении SignalR на C#

После запуска приложения в разных браузерах при отправке сообщения каждый браузер будет получать отправленное сообщение и выводить его на веб-страницу:

Первое приложение на SignalR на ASP.NET Core и C#

Расширение приложения

Теперь модифицируем приложение, что кроме сообщения пользователя также передавалось и его имя. Вначале изменим код класса ChatHub:

using Microsoft.AspNetCore.SignalR; namespace SignalRApp < public class ChatHub : Hub < public async Task Send(string message, string userName) < await Clients.All.SendAsync("Receive", message, userName); >> >

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

И изменим страницу index.html :

    Metanit.com   
Введите логин:


Введите сообщение:


Так как хаб на сервере отправляет клиентам два значения — имя пользователя и его сообщение, то соответственно на стороне клиента в функции, которая регистрируется в методе hubConnection.on мы можем получить оба этих значения.

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

SignalR в помощь, или как оживить web

Во многих web проектах присутствуют элементы, значение которых необходимо часто менять. Это могут быть счётчики, индикаторы, уведомления и подобные элементы. Показывать ли актуальные значения после обновления страницы или же можно реализовать автообновление всех этих данных? Для нас ответ очевиден: если есть возможность динамически менять элементы, то для обновления страниц не остается места.

Для небольших проектов, которые не подвержены критичным нагрузкам, пока у них нет нескольких тысяч пользователей онлайн в часы пик, приемлемым решением было бы использовать AJAX. Логика для этого решения следующая: клиент с заданной периодичностью опрашивает сервер, в поисках обновлений на странице, если сервер сообщил, что есть обновленные данные, то javascript внесен обновления в элементы страницы или отобразит уведомление. То действие, которое будет наиболее подходящим.

Но для больших проектов решение с AJAX, где каждый клиент будет опрашивать сервер, это создаст слишком большую нагрузку. Конечно, мы можем оптимизировать свои мощности и создать цепочку серверов по всей стране, которые будут готовы обрабатывать все запросы клиентов. Это не наш метод. Мы хотим, что бы сервер сам оповещал клиента о новых данных. Подобная практика используется в Desktop-приложениях — cервер, к которому подключаются клиенты при помощи сокетов. Эта логика пригодилась бы нам и в web. Уже есть Websockets, с которыми можно работать, даже .Net взял под крыло поддержку websockets. Но, объективно о повседневном использовании websockets говорить еще рано. Нужно что-то ещё. Возможно использование longpolling, где мы откроем соединение на клиенте и не будем закрывать его вовсе, ожидая события от сервера. Нет, продолжаем искать дальше.

Мы обратили внимание на SignalR. Опишем, как он работает. SignalR может использовать в качестве транспорта и websockets, и longpolling. Транспорт можно задать, а можно оставить на откуп SignalR, который сам выберет нужный. В случае, если можно использовать websocket, то он будет работать через websocket, если такой возможности нет, то он будет спускаться дальше, пока не найдёт приемлемый транспорт.

Более подробно рассмотреть работы SignalR можно при реализации определенной задачи. Что у нас есть: проект, с зарегистрированными пользователями, у каждого пользователя есть личный кабинет, в котором есть раздел сообщений, адресованных этому пользователю. Там же есть и счётчик новых сообщений. Мы хотим, чтобы как только один пользователь (user1) отправил сообщение другому пользователю (user2), на открытой странице пользователя user2 сразу же обновился счётчик новых сообщений.

Чтобы приступить к реализации, нам нужно подключить SignalR к нашему проекту. Как это сделать можно посмотреть на странице signalR, там же можно найти и необходимую документацию.
Если вы используете NuGet, то достаточно будет выполнить:

Install-Package Microsoft.AspNet.SignalR -pre 
Работаем

SignalR будет использовать серверную и клиентскую части. На сервере самой сутью является Хаб (Hub). Это Класс, который позволит нам описать поведение SignalR. Так это выглядит в нашем примере:

[HubName("msg")] public class Im : Hub < public Task Send(dynamic message) < return Clients.All.SendMessage(message); >public void Register(long userId) < Groups.Add(Context.ConnectionId, userId.ToString(CultureInfo.InvariantCulture)); >public Task ToGroup(dynamic id, string message) < return Clients.Group(id.ToString()).SendMessage(message); >> > 
  • Send — отправит всем клиентам сообщение.
  • Register – поможет SignalR найти того или иного пользователя, которому нужно отослать сообщение. Здесь можно увидеть использование Groups, расскажем об этом позже.
  • ToGroup – отправит сообщение той или иной группе пользователей. Говоря «пользователи» мы подразумеваем «Группа, объединяющие соединения».
    

Подробнее о клиентском коде. SignalR сгенерирует для нас хаб. Его можно посмотреть для любопытства /signalr/hubs

В коде, при запуске хаба (для этого используется метод start), мы обращаемся к методу register. Мы уже использование его в серверном коде. Как будет получен UserId или другой идентификатор сейчас не принципиально. Мы используем самое простое решение для простоты понимания. Если мы все сделали правильно, то при обращении к странице с нашим клиентским кодом, стартует хаб, и методом register сообщает серверу, что пользователь userId подключился.

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

Для нас доступны следующие возможности:

// разослать всем Clients.All.send(message); // всем, кроме меня Clients.Others.send(message); // всем, кроме определённого идентификатора соединения Clients.AllExcept(Context.ConnectionId).send(message); // тому, кто прислал Clients.Caller.send(message); // отослать всем в группе "foo" Clients.Group("foo").send(message); // отослать всем из группы "foo", кроме того кто прислал Clients.OthersInGroup("foo").send(message); // отослать всем из группы "foo", кроме определённого идентификатора соединения Clients.Group("foo", Context.ConnectionId).send(message); // отослать клиенту с определённым идентификатором соединения Clients.Client(Context.ConnectionId).send(message); 

Важный момент: при каждом новом обращении к странице (перезагрузка страницу), в том числе и открытие её в новой вкладке, создает соединение с новым идентификатором соединения SignalR. Это означает, что идентификатора UserId будет недостаточно для того, чтобы оповестить пользователя в каждой открытой вкладке браузера. Нам нужно оповестить все ConnectionId, принадлежащие к пользователю UserId. Для этого в нашем примере класса хаба, в методе Register мы добавили группу с именем UserId каждый новый ConnectionId. Теперь мы можем оперировать UserId как именем группы и оповещать все ConnectionId пользователя.

Частная практика

Давайте рассмотрим еще одну ситуацию. Пользователь user2 написал сообщение пользователю user1 и нажал кнопку «Отправить». Какие наши дальнейшие действия? Можно написать дополнительные методы класса хаба и отправлять сообщения при помощи SignalR. Хаб примет сообщение, обработает и оповестит пользователя. Помимо отправки самого сообщения с этим действием, как правило, связана дополнительная логика: запись сообщения в базу данных, логирование, отправка сообщения на модерацию и многое другое. Могут быть и более сложные примеры. Поэтому, мы ограничимся использованием хаба только по назначению: прием и отправка сообщения. Сначала мы воспользуемся старым добрым AJAX-ом и отправим сообщение от пользователя на HttpHandler. Затем сделаем с ним всё, что необходимо (запишем в базу данных или отправим на модерацию) и в итоге отправим хабу, который оповестит пользователя user1. Но есть сложность — HttpHandler находится в недрах одной из многочисленных библиотек, совсем в другом проекте. Воспользуемся возможностями SignalR чтобы устранить эту сложность. Создадим прокси-класс для соединения с хабом:

static HubConnection connection; static IHubProxy hub; static string Url = "http://im.myProjectSite.com"; // адрес нашего хаба connection = new HubConnection(Url); hub = connection.CreateHubProxy("msg"); connection.Start().Wait(); hub.Invoke("ToGroup", userId, message); 

Посмотрите на использование метода Invoke. Мы вызываем метод ToGroup нашего хаба, который разошлет нужное сообщение по всем соединениям (connectioId), сопоставленными с нужным пользователем UserId. Здесь мы так же задействовали объекты static. Достаточно при старте приложения инициализировать прокси к хабу, скажем, в global.asax и при необходимости вызывать метод, в котором происходит Invoke.

С появлением SignalR в .Net, появилась необходимость в добавлении ещё нескольких строчек кода:

RouteTable.Routes.MapHubs("~/signalr"); RouteTable.Routes.MapHubs(); GlobalHost.HubPipeline.EnableAutoRejoiningGroups(); 

Использование роутинга SignalR несколько выходит за рамки нашей задачи. Скажем только, что EnableAutoRejoiningGroups поможет нам не потерять соединение в группе для пользователя при создании нового соединения.

С чем Вы обязательно столкнётесь и как это решить

После того как мы разобрались с описанными примерами и собрали демо-проект (или внедрили в существующий) мы пытаемся проверить его на работоспособность. Если всё сделано правильно, пользователь будет оповещен о новом сообщении, как мы этого и хотели. Мы можем даже открыть несколько вкладок браузера, чтобы убедиться в том, что оповещение приходит во все вкладки (все connectionId пользователя). Но стоит нам открыть чуть больше вкладок, как мы обнаружим, что на N вкладке уже ничего не работает. Для разных обозревателей N разное (4 или 6, слишком маленькое). Это ограничение не позволяют создавать больше N одновременных соединений к одному хабу. В некоторых проектах, даже достаточно крупных, можно увидеть решение, в котором пользователю сообщается, что он уже где-то открыл подобный диалог и предложение либо перейти обратно на старый, либо выключить старый и переключиться на этот. Ограничение не должно влиять на пользователя, он может открыть столько вкладок, сколько ему угодно и мы покажем уведомления в каждой из них. Чтобы это работает, нам необходимо каким-то образом дать понять браузеру, что используются разные хабы, а не на один. Ранее при инициализации хаба мы указали его Url: im.myProjectSite.com. Создадим обычные зеркала:

im1.myProjectSite.com im2.myProjectSite.com im3.myProjectSite.com 

А в клиентском коде мы будем подставлять адрес хаба по определенному алгоритму. Самым простым способом будет каждый раз при обращении к странице (при открытии в новой вкладке), подставлять im (j+1), и j=1 снова после im3. В этом примере мы увеличили ограничение до 3N.

Заключение

Мы описали только общие принципы, используя самые простые примеры.
При использовании в своих проектах не забывайте особое внимание обратить на безопасность. Если посмотреть на метод Register нашего хаба, то станет очевидным, что любой желающий сможет вызвать этот метод не под своим UserId, чего нам с Вами совсем не хочется.

Автор статьи: Дмитрий Лубенский, ведущий разработчик Дневник.ру. Отвечает за работу биллинговой системы и участвует в развитии международного направления.

  • Блог компании «Дневник.ру»
  • Веб-разработка
  • .NET

Что такое SignalR?

ASP.NET SignalR — это библиотека для ASP.NET разработчиков, которая упрощает добавление в приложения компонентов, работающих в реальном времени. Функциональность, работающая в реальном времени – это способность сервера отдать свежие данные подключенным клиентам немедленно, вместо того, чтобы ждать пока клиенты запросят эти данные.

SignalR может быть использован для добавления в ASP.NET приложения любого вида веб-функциональности, работающей в реальном времени. Сразу напрашивается пример с чатом на сайте, но с SignalR можно делать гораздо больше. Каждый раз, когда пользователь обновляет страницу, чтобы получить новые данные или страница применяет технику long polling (открывается соединение на клиенте и не закрывается совсем, ожидая события от сервера), это явные кандидаты для использования SignalR. Примерами могут также быть панели мониторинга (dashboards), приложения для совместной работы (например совместное редактирование документов), получение актуальных данных о выполнении какой-либо работы или формы ввода данных в реальном времени.

SignalR также раскрывает просторы для нового типа веб-приложений, где требуется быстрый обмен данными с сервером, а это игровые приложения в реальном времени.

SignalR имеет простой API для вызовов удаленных процедур от сервера к клиенту (RPC server-to-client), которые вызывают Javascript функции в клиентских браузерах из кода .NET сервера. SignalR также имеет API для управления соединениями (например подключением или отключением) и группировкой соединений.

SignalR управляет соединениями автоматически, и отсылает сообщения всем подключенным клиентам одновременно, как в чате. Возможно также отсылать сообщения только определенным клиентам. Соединение между клиентом и сервером постоянное, в отличии от классического HTTP соединения, которое повторно устанавливает связь для каждого подключения.

SignalR поддерживает функциональность «server push» (толкни сервер), когда код на сервере может отправить сообщения в браузеры, используя Remote Procedure Calls (RPC), это быстрее работает, чем обычная модель «запрос-ответ», существующая в вебе сегодня.

SignalR приложения могут масштабироваться на тысячах клиентах, используя Service Bus, SQL Server или Redis.

SignalR поставляется с открытым кодом, который доступен на GitHub.

SignalR и WebSocket

SignalR использует новый протокол обмена сообщениями WebSocket, там где это возможно, и переходит на более старые протоколы, там где это необходимо. Конечно можно написать приложение напрямую используя WebSocket, но с SignalR большую часть функциональности уже не придется реализовывать. Самое главное с SignalR не надо будет беспокоиться о написании кода для поддержки старых протоколов обмена сообщениями. SignalR также избавит от необходимости обновления протокола WebSocket. И будет поддерживать приложение в целостном состоянии при переходе на новые версии.

SignalR обеспечит всю ту функциональность, которую придется писать разработчику самому, использующему только WebSocket, а это поддержка старых транспортных протоколов, а также ревизия и правка кода приложения для новых версий протокола WebSocket.

Что такое SignalR? ➤ В чем разница между ASP.NET SignalR и ASP.NET Core SignalR?

Что такое SignalR? ➤ В чем разница между ASP.NET SignalR и ASP.NET Core SignalR?

SignalR Tutorial 1 — What is SignalR?

Серия ASP.NET Core: SignalR

ASP.NET Core SignalR – SMART TALKS 236: .NET, C#

Build Chat Room using ASP.NET Core SignalR

Веб-приложения в реальном времени с ASP.NET Core SignalR

Александр Кузнецов «Введение в SignalR»

Взаимодействие в реальном времени с помощью SignalR

Создание Real-Time web приложений с библиотекой ASP.NET SignalR

SignalR Tutorial 12 — Hubs and Persistent Connections (Концентраторы и постоянные подключения)

SignalR Deep Dive: создание серверов — Дэвид Фаулер и Дэмиан Эдвардс

Разница между ser и estar

В чем разница между саркастическим и сардоническим?

Как разница между I wear и I am wearing?

Разница между beside и besides. Английский для путешествий

Разница между BESIDES, EXCEPT и APART FROM. Английский для путешествий

разница между глаголами estar и hay

В чем разница между b2b и b2c продажами полный

Разница между FBA и FBM в бизнесе на Амазон

Есть ли разница между mp3 и FLAC ( Lossless ) ?

В чем разница между кондиционером и сплит-системой

Разница между Scania R и S [обзор 1.30 beta] ETS 2

Разница между компонентом и телом во Fusion 360

Тутанота объяснил: разница между псевдонимом и пользователем

Джон Вуден: Разница между победой и успехом

Аспаркам. Применение. Разница между аспаркамом и панангином

Дэнни Гилл — Разница между Overdrive и Distortion

В чем разница между Track, Cyclocross и Road bikes

Разница между АК 47 и АК 74 / / Brandon Herrera на Русском Языке.

Самый престижный боксерский пояс. Разница между поясами WBA WBC WBO IBF

Справка EV: варианты двигателей Renault Zoe — в чем разница между двигателями Q и R?

Разговор про новую сцепку Scania. Разница между S и R серией.

Octopus Box или Octoplus, что лучше, и какая между ними разница?

C — log на Canon R 6 Как настроить, как работать на clog, какая разница между 8 бит и 10 бит

Артрит и артроз — в чем разница между этими болезнями суставов. Их симптомы

ОБЗОР ОБЩЕЖИТИЙ ПРАГИ. РАЗНИЦА МЕЖДУ ГОСУДАРСТВЕННЫМИ И ЧАСТНЫМИ ОБЩЕЖИТИЯМИ

Разница между типами сжатия ALL i и IPB Кодек Canon EOS R6 IPB и ALL i Можете ли вы сказать?

Разница во времени между городами мира. Узнайте разницу во времени между городами мира

Searches
  • что такое любовь
  • mma core
  • core
  • core temp
  • между двух миров
  • между нами тает лед
  • между нами девочками
  • между нами горы
  • y8.com 1
  • newsge
  • სენატი
  • გრეის ანატომია 13 სეზონი ქართულად

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *