Создание современных приложений с применением SignalR

OSzone.net » Microsoft » Разработка приложений » Языки программирования » Создание современных приложений с применением SignalR
Автор: Рэчел Аппель
Иcточник: msdn.microsoft.com
Опубликована: 29.09.2015
С широким распространением широкополосного Интернета и беспроводных устройств нарастает спрос на приложения реального времени. Такие популярные сайты, как Facebook и Twitter, многопользовательские игры и бизнес-приложения для коллективной работы наиболее удобны в использовании, когда они быстро отвечают и работают как программы реального времени. Многие другие типы приложений являются отличными кандидатами для работы в реальном времени, например биржевые и финансовые приложения, аукционы, электронная коммерция, образовательные приложения и др. Даже веб-сайты и приложения, где постоянное получение актуальных данных не требуется, могут выиграть от полнодуплексного взаимодействия в реальном времени с помощью SignalR.

Что такое SignalR и зачем его использовать?

SignalR — это набор серверных и клиентских библиотек, облегчающих двухстороннее взаимодействие реального времени между сервером и клиентом. Не только клиент может инициировать контакт с сервером, как в случае веб-разработки, но и сервер может связываться с клиентом. При этом используются отнюдь не просто HTTP-ответы. На самом деле это вызовы методов с сервера на клиенте по аналогии с технологией оповещения (push technology). Клиент может даже связываться даже с другими клиентами через серверный компонент SignalR. Все это возможно потому, что SignalR создает постоянное соединение между сервером и клиентом.

Все хотят создавать современное программное обеспечение. А что может быть современнее полнодуплексного взаимодействия? Использовать SignalR стоит по нескольким причинам. Одна из веских причин — простота написания веб-сайтов и приложений при его применении. Другая причина — в вашем приложении требуется взаимодействие в реальном времени. В таких случаях лучше всего использовать SignalR. Вы могли бы сделать это сами, используя любые другие технологии, например WebSockets или AJAX-опросы. Однако вам пришлось бы заново писать всю фундаментальную инфраструктуру, уже написанную группой SignalR. Эта инфраструктура весьма обширна и характеризуется несколькими ключевыми особенностями.

Веб-разработчики традиционно пишут код согласно HTTP-модели «запрос-ответ». В этом нет ничего плохого, но отсутствует главное преимущество SignalR: постоянное соединение между сервером и клиентом. В HTTP вы выдаете запрос, получаете ответ, и на этом все. В сценарии реального времени конвейер остается открытым между сервером и клиентом. Это позволяет создавать более функциональные и удобные пользовательские среды, которые кажутся «живыми».

В SignalR два уровня абстракции поверх низкоуровневых транспортов: концентраторы (hubs) и постоянные соединения (persistent connections). В этой статье для краткости я расскажу только о концентраторах. Концентраторы — это API более высокого уровня, который является «невероятно простой» частью SignalR. Постоянные соединения требуют больше времени и усилий в кодировании, и SignalR использует их как основу для концентраторов. В целом, для большинства своих операций вы будете использовать концентраторы, если только у вас не появятся веские причины поступить иначе.

Приступаем к работе с SignalR в Windows-приложениях

Как и многие другие .NET-библиотеки, SignalR представляет собой NuGet-пакет. Вы можете установить его с помощью NuGet Package Manager или Package Manager Console. Оба эти средства входят в Visual Studio 2012 и Visual Studio 2013. Microsoft предлагает несколько разных пакетов SignalR, включая следующие.

При установке пакета Microsoft ASP.NET SignalR в любой из ваших проектов ASP.NET (Web Forms или MVC) SignalR устанавливает зависимости в каждом из перечисленных пакетов, кроме .NET Client. Пакет .NET Client предназначен для XAML-приложений Windows 8 и Windows Phone, а также приложений Windows Presentation Foundation (WPF), Windows Forms и консольных программ. В NuGet Package Manager есть и другие пакеты SignalR как от Microsoft, так и от сторонних компаний. Они охватывают почти все, в том числе поддержку резидентных серверов, масштабирование, встраивание зависимостей и базы данных MongoDB.

После установки никакие изменения в web.config вносить не требуется. Однако вы должны добавить небольшой фрагмент стартового кода, чтобы указать ASP.NET, что вы включите SignalR в ее конвейер:

[assembly: OwinStartup(typeof(VoteR.Startup))]
public partial class Startup
{
  public void Configuration(IAppBuilder app)
  {
    app.MapSignalR();
  }
}

Вы можете добавить этот класс Startup в файл .cs в папке App_Start. Некоторые шаблоны проектов ASP.NET уже включают класс Startup для аутентификации в ASP.NET на основе форм. Если это так, просто добавьте метод Configuration к этому классу. После этого можно переходить к написанию кода, работающего в реальном времени.

Этот класс Startup является стартовым для Open Web Interface for .NET (OWIN). Это означает, что он отвечает новым спецификациям OWIN. OWIN — это стандарт, во многом похожий на стандарты, контролируемые Worldwide Web Consortium (W3C). Microsoft реализовала OWIN в проекте Katana. Это механизм за SignalR, который работает в тандеме с IIS или как самостоятельный хост и облегчает двухсторонние коммуникации. Как разработчику для SignalR, использующему концентраторы, вам не обязательно знать что-то еще об OWIN или Katana. SignalR абстрагирует все это, и вы можете сосредоточиться на решении своих бизнес-задач с помощью SignalR.

Серверная сторона концентраторов SignalR

Концентраторы — основа коммуникаций для SignalR. Они принимают входящие запросы и отправляют сообщения клиентам либо с самого концентратора, либо в интересах другого клиента. Концентратор в SignalR можно рассматривать как спицы колеса.

Концентратор является просто контроллером шлюза для обмена сообщениями. Занимая центральное место, концентраторы — это просто классы, производные от класса Microsoft.AspNet.SignalR.Hub. Класс Hub в свою очередь реализует интерфейс IHub пространства имен Microsoft.AspNet.SignalR.Hubs. Интерфейс IHub определяет три события: OnConnected, OnDisconnected и OnReconnected. Кроме того, в нем определены три свойства: Clients, Context и Groups. Они отражают распространенные задачи или информацию, относящуюся к каждому соединению реального времени с концентратором.

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

Свойство Clients представляет набор всех подключенных клиентов. Через него можно обращаться к какому-то одному или нескольким клиентам независимо от их платформы. Например, iOS-клиент может отправить сообщение Windows-клиенту через концентратор, поскольку тот взаимодействует с Windows-клиентом в интересах iOS-клиента и наоборот.

Чтобы показать концентратор в действии, я исследую приложение-пример VoteR, которое отображает серии элементов и позволяет пользователям голосовать за своих фаворитов. В основе этого приложения лежит класс VoteHub. Это концентратор, который подсчитывает число голосов, отданных пользователями за каждый элемент. Затем он уведомляет клиенты об обновлении числовых показателей. На рис. 1 представлен класс VoteHub.

Рис. 1. Класс VoteHub подсчитывает голоса

public class VoteHub : Hub
{
  private static List<Item> VoteItems = new List<Item>();
  private static VoteRContext db = new VoteRContext();
  public void Vote(int id)
  {  
    var votes = AddVote(id);
    Clients.All.updateVoteResults(id, votes);
  }
  private static Item AddVote(int id) {
    var voteItem = VoteItems.Find(v => v.Id == id);       
    if (voteItem != null)
    {
      voteItem.Votes++;
      return voteItem;
    }
    else
    {
      var item = db.Items.Find(id);
      item.Votes++;
      VoteItems.Add(item);
      return item;  
    }       
  }
  public override Task OnConnected()
  {       
    Clients.Caller.joinVoting(VoteItems.ToList());
    return base.OnConnected();
  }
}

Давайте рассмотрим два метода на рис. 1: Vote и AddVote. Метод Vote — то, что вызывают клиенты. Затем приложение вызывает закрытый метод AddVote, который и выполняет подсчет голосов. Для этого он проверяет, есть ли в списке VoteItems какие-нибудь элементы. Если есть, он их обновляет. Если нет, то при первом голосе за какой-то элемент, AddVote добавляет его в список. Статический List<Vote> — простой способ хранения такой глобальной информации без базы данных.

Метод Vote на рис. 1 содержит интересную строку кода вслед за вызовом AddVote:

Clients.All.updateVoteResults(id, votes);

В SignalR вы будете использовать свойство Clients для обращения к коду на клиенте и его вызова. Через свойство Clients вы можете адресоваться к нужным вам клиентам. В каких-то случаях это будут все клиенты, например при получении нового голоса. В других случаях вам понадобится только один клиент, скажем, при первом подключении пользователя. Применение соглашений по именованию позволяет SignalR соотносить вызов сервера с кодом, который должен выполняться на клиенте. Так что впервые в истории ASP.NET вы можете использовать динамические свойства для вызова кода на клиенте.

Как вы можете представить, поскольку VoteHub нужно отслеживать голоса, есть смысл в таком событии, как OnConnected. Это событие позволяет захватывать новые входящие соединения. Вероятный сценарий — захват идентификатора соединения через свойство ConnectionId объекта Context.

В случае приложения VoteR на рис. 1 оно в цикле перебирает три элемента в объекте VoteHub List<Item> и уведомляет клиент, сколько голосов получил каждый элемент, через свойство Clients.caller. Тем самым только что подключенные клиенты сразу же узнают об общем количестве голосов за каждый элемент.

Есть много других способов взаимодействия между сервером и клиентом. Свойство Clients класса Hub позволяет обращаться к клиентскому коду самыми разнообразными способами, кратко обозначенными на рис. 2.

Рис. 2. Различные способы взаимодействия сервера с клиентом

// Вызов метода во всех клиентах
Clients.All.clientSideMethod(args, args, ...);
// Вызов метода в конкретном клиенте
Clients.Client(Context.ConnectionId).clientSideMethod( args, args, ...);
// Вызов метода в списке конкретных соединений
Clients.Clients(ConnectionId1, ConnectionId1, ...). clientSideMethod(args, args, ...);
// Вызов метода в вызвавшем соединении
Clients.Caller.clientSideMethod(args, args, ...);
// Вызов метода во всех клиентах, кроме вызвавшего
Clients.Others. clientSideMethod(args, args, ...);
// Вызов метода во всей группе,
// кроме нескольких выбранных соединений
Clients.AllExcept(connectionId1, connectionId2). clientSideMethod(args, args, ...);
// Вызов метода в группах соединений
Clients.Group(groupName).clientSideMethod(args, args, ...);
// Вызов метода в подключенных клиентах,
// которые относятся к указанной группе
Clients.Groups(GroupIds).clientSideMethod(args, args, ...);
// Вызов метода в других подключенных клиентах,
// которые относятся к указанной группе
Clients.OthersInGroup(groupName).clientSideMethod( args, args, ...);

На рис. 2 clientSideMethod определен в клиенте, который вызывается сервером. В следующем разделе вы узнаете, как определить эти методы на клиенте. Как видите, динамическая природа свойства Clients позволяет писать код для широкого спектра сценариев взаимодействия сервера с клиентами.

Клиентская сторона SignalR: JavaScript- и .NET-клиенты

Вы можете создавать приложения реального времени на любой платформе с SignalR. В готовом виде вы можете использовать SignalR JavaScript-клиент для любых клиентских веб- и HTML-приложений, в том числе для WinJS-приложений. Чистый HTML и JavaScript — широко поддерживаемые языки. Для тех, кто использует .NET, Microsoft выпустила .NET-клиент как для Windows-приложений, так и для настольных приложений. Как и базовые компоненты, вы устанавливаете либо JavaScript Client, либо .NET Client из NuGet в зависимости от типа своего проекта. Поскольку JavaScript — это просто JavaScript, вы можете скачать скрипты из github.com/SignalR/SignalR и добавить теги <script> на свою страницу, не ссылаясь на файлы .dll, как показано ниже:

<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.signalR-2.0.3.js"></script>
<script src="~/signalr/hubs"></script>

Порядок ссылок на скрипты важен. Вы должны загрузить jQuery первой, потому что SignalR зависит от нее. Следующий в строке — клиент SignalR. Последний — прокси SignalR. SignalR динамически генерирует прокси в период выполнения и скидывает его в /signalr/hubs. Этот прокси позволяет писать код и на клиенте, и на сервере, при этом он ведет себя так, будто все находится в одном месте.

Клиентский скрипт приложения VoteR определяет методы, принимающие вызовы от сервера, а также обычные подключения методов и событий. На рис. 3 в самой первой строке кода захватывается переменная voteHub. Она прямо ведет к экземпляру класса VoteHub. SignalR создает экземпляр концентратора для каждого клиента, который подключается. Клиент запускает соединение вызовом $.connection.hub.start, которая возвращает обещание (promise). Это означает, что код внутри не будет выполняться, пока обещание не завершено. В данном случае это вызов метода Vote на сервере внутри обработчика события click кнопки голосования. Как видите, он передает на сервер идентификатор элемента, за который голосует пользователь. Затем сервер выполняет работу, очерченную на рис. 1.

В этот момент вы можете подумать, будто на рис. 3 есть опечатки. Это вызвано тем, что именование класса VoteHub и метода Vote не согласуется между сервером и клиентом. Так что это не опечатка, а соглашение SignalR. В JavaScript-клиентах вызовы hub.server.methodName по умолчанию записываются согласно нотации «camelCase». Это поведение достаточно легко изменить, подключив атрибут HubName к классу Hub с точным использованием прописных букв, нужным вам. Атрибут HubName выглядит как HubName("VoteHub").

Рис. 3. Код JavaScript-клиента

$(function () {
  var voteHub = $.connection.voteHub;
  $.connection.hub.start().done(function () {
    $("button").click(function () {
      voteHub.server.vote(this.id);
        });
  });
  voteHub.client.updateVoteResults = function (id, vote) {
   // Обновляем UI для отображения каждого элемента
    // и количества отданных за него голосов
    }
  voteHub.client.joinVoting = function (votes) {
    // Перебираем в цикле голоса для отображения
    // текущей информации о каждом элементе для новых клиентов
  }   
});

Два наиболее интересных блока кода на рис. 3 — это методы voteHub.client.updateVoteResults и voteHub.client.joinVoting. Как указывают их сигнатуры, оба метода являются членами свойства Client класса VoteHub на сервере. Если вернуться к рис. 1, то клиентский метод voteHub.client.updateVoteResults с рис. 3 соответствует вызову Clients.All.updateVoteResults(id, votes) с рис. 1. На рис. 4 показана взаимосвязь между серверным и клиентским кодом.

*
Рис. 4. Взаимосвязь между вызовами методов концентратора и клиента

ProxyПрокси


Теперь пора изучить .NET-клиент. На рис. 5 приведен некоторый код, который устанавливает соединение из XAML-приложения Windows Store, используя C#. Эта операция так же проста в приложении Windows Phone, поскольку код для них идентичен. Он начинает с создания соединения с конвейером SignalR. Затем создает прокси.

Рис. 5. Клиентский код на C# для приложения Windows Store, обеспечивающий голосование в VoteR

async private Task startConnection()
{
  var hubConnection = new HubConnection("http://localhost:25024/signalr");
  IHubProxy hubProxy = hubConnection.CreateHubProxy("VoteHub");
  var context = SynchronizationContext.Current;
  hubProxy.On<string, string>("updateVoteResults", (id, votes) =>
    context.Post(delegate {
   // Обновляем UI
); }, null));                        
  await hubConnection.Start();
  await hubProxy.Invoke("vote", "rachel",
    Convert.ToDecimal(itemId.Text));
}

На рис. 5 показано то, что обычно вы не увидите в JavaScript-клиенте, а именно: HTTP-путь к конвейеру SignalR, передаваемый в конструктор HubConnection. В отличие от JavaScript-клиента здесь не все делается автоматически. Вы должны создать экземпляр HubConnection и вызвать CreateHubProxy для создания прокси.

Обратите внимание на рис. 5 на присваивание, благодаря которому вы получаете контекст синхронизации. Вы обертываете клиентский код, вызываемый сервером, в этот объект контекста. Затем вы вызываете его метод Post и передаете делегат. Делегаты в C# — это то же самое, что подставляемые в строку анонимные функции в JavaScript.

После запуска соединения с концентратором вы можете вызвать метод Invoke его прокси, чтобы проголосовать за какой-либо элемент. Использование ключевого слова await заставляет его выполнять эти действия асинхронно. Полный исходный код демонстрационного приложения VoteR см. по ссылке github.com/rachelappel/VoteR.

Развертывание SignalR: сервер и клиент

Поскольку SignalR — это ASP.NET, вы должны развернуть его в среде с .NET Framework 4.5 или выше, если только вы не используете собственный хост (self-host). В проекте ASP.NET SignalR представляет собой просто набор библиотек.

SignalR можно развернуть в Microsoft Azure. Применение Azure делает процесс развертывания особенно беспроблемным. При этом также развертываются серверный компонент SignalR и минимум один HTML-клиент на веб-сервер. Конечно, вы должны опубликовать любые приложения Windows Store или Windows Phone в App Store, а настольные приложения — на персональных компьютерах по соответствующим каналам.

Те, кто работает с любыми проектами ASP.NET, могут выбрать Publish из меню Build в Visual Studio для запуска развертывания в Azure. Если вы используете Visual Studio 2013, вы можете просто следовать предложениям. Вам нужно лишь ввести свои удостоверения и выбрать базу данных и имя веб-сайта.

В Visual Studio 2012 выводит похожий набор предложений. При развертывании можно выбрать создание нового веб-сайта в Azure или указать существующий. Если это существующий сайт, войдите в Azure Portal, перейдите на вкладку Configuration, найдите и включите WebSockets. Вы должны сделать это и с новым веб-сайтом, но Visual Studio сначала создаст и запустит сайт, что приведет к ошибке. И вновь просто войдите на портал и включите WebSockets. Это важный момент. Хорошая идея — останавливать любые веб-сайты перед конфигурационными изменениями и вновь запускать их после внесения таких изменений.

Заключение

SignalR — по-настоящему простая среда программирования реального времени. Хотя это продукт ASP.NET, он является кросс-платформенным в том плане, что вы можете писать приложения Windows Store, iOS и Android с помощью серверного компонента ASP.NET. Кроме того, вы можете написать собственный сервер в операционных системах, отличных от Microsoft. Это делает SignalR гибким, а также легким в использовании и эффективным. Другое крупное преимущество SignalR в том, что вам даже не обязательно иметь функциональность реального времени. Просто используйте его и присоединяйтесь к тем, кто уже принял парадигму программирования реального времени.


Ссылка: http://www.oszone.net/28040/