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

В этом руководстве показано, как создать веб-приложение, которое использует ASP.NET SignalR 2 для предоставления функций высокочастотного обмена сообщениями. В этом случае "высокочастотный обмен сообщениями" означает, что сервер отправляет обновления с фиксированной скоростью. Вы отправляете до 10 сообщений в секунду.

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

Основные понятия, представленные в этом руководстве, содержат приложения в играх в режиме реального времени и другие приложения для моделирования.

Изучив это руководство, вы:

  • Настройка проекта
  • Создание базового приложения
  • Сопоставление с центром при запуске приложения
  • Добавление клиента
  • Запуск приложения
  • Добавление цикла клиента
  • Добавление цикла сервера
  • Добавление плавной анимации

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

Эта документация не подходит для последней версии SignalR. Ознакомьтесь с ASP.NET Core SignalR.

Предварительные требования

  • Visual Studio 2017 с рабочей нагрузкой ASP.NET и веб-разработка.

Настройка проекта

В этом разделе описано, как создать проект в Visual Studio 2017.

В этом разделе показано, как использовать Visual Studio 2017 для создания пустого веб-приложения ASP.NET и добавления библиотек SignalR и jQuery.UI.

  1. В Visual Studio создайте веб-приложение ASP.NET.

    Создание веб-сайта

  2. В окне Новое веб-приложение ASP.NET — MoveShapeDemo оставьте флажок Пустой и нажмите кнопку ОК.

  3. В Обозреватель решений щелкните проект правой кнопкой мыши и выберите добавить>новый элемент.

  4. В разделе Добавление нового элемента — MoveShapeDemo выберите Установленные>visual C#>Web>SignalR, а затем — Класс концентратора SignalR (версия 2).

  5. Назовите класс MoveShapeHub и добавьте его в проект.

    На этом шаге создается файл класса MoveShapeHub.cs . Одновременно он добавляет в проект набор файлов скриптов и ссылок на сборки, которые поддерживают SignalR.

  6. Выберите Инструменты>Диспетчер пакетов NuGet>Консоль диспетчера пакетов.

  7. В консоли диспетчера пакетов выполните следующую команду:

    Install-Package jQuery.UI.Combined
    

    Команда устанавливает библиотеку пользовательского интерфейса jQuery. Вы используете его для анимации фигуры.

  8. В Обозреватель решений разверните узел Скрипты.

    Ссылки на библиотеку сценариев

    Библиотеки сценариев для jQuery, jQueryUI и SignalR видны в проекте.

Создание базового приложения

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

  1. Откройте файл MoveShapeHub.cs .

  2. Замените код в файле MoveShapeHub.cs следующим кодом:

    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class MoveShapeHub : Hub
        {
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);
            }
        }
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
            [JsonProperty("top")]
            public double Top { get; set; }
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
    }
    
  3. Сохраните файл.

Класс MoveShapeHub является реализацией концентратора SignalR. Как и в руководстве по начало работы с SignalR, концентратор имеет метод, который клиенты вызывают напрямую. В этом случае клиент отправляет на сервер объект с новыми координатами X и Y фигуры. Эти координаты передаются всем другим подключенным клиентам. SignalR автоматически сериализует этот объект с помощью JSON.

Приложение отправляет ShapeModel объект клиенту. Он содержит элементы для хранения положения фигуры. Версия объекта на сервере также имеет член для отслеживания хранимых данных клиента. Этот объект предотвращает отправку сервером данных клиента обратно самому себе. Этот член использует атрибут , JsonIgnore чтобы приложение не сериализовать данные и отправить их обратно клиенту.

Сопоставление с центром при запуске приложения

Затем вы настроите сопоставление с концентратором при запуске приложения. В SignalR 2 при добавлении класса запуска OWIN создается сопоставление.

  1. В Обозреватель решений щелкните проект правой кнопкой мыши и выберите добавить>новый элемент.

  2. В разделе Добавление нового элемента — MoveShapeDemo выберите Установленный>Visual C#>Web , а затем выберите Класс запуска OWIN.

  3. Присвойте классу имя Startup и нажмите кнопку ОК.

  4. Замените код по умолчанию в файле Startup.cs следующим кодом:

    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))]
    namespace MoveShapeDemo
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Any connection or hub wire up and configuration should go here
                app.MapSignalR();
            }
        }
    }
    

Класс запуска OWIN вызывает, MapSignalR когда приложение выполняет Configuration метод . Приложение добавляет класс в процесс запуска OWIN с помощью атрибута сборки OwinStartup .

Добавление клиента

Добавьте HTML-страницу для клиента.

  1. В Обозреватель решений щелкните проект правой кнопкой мыши и выберите Добавить>HTML-страницу.

  2. Присвойте странице имя Default и нажмите кнопку ОК.

  3. В Обозреватель решений щелкните правой кнопкой мыши Default.html и выберите Пункт начальной страницы.

  4. Замените код по умолчанию в файле Default.html следующим кодом:

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
     $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                $shape = $("#shape"),
                shapeModel = {
                    left: 0,
                    top: 0
                };
                moveShapeHub.client.updateShape = function (model) {
                    shapeModel = model;
                    $shape.css({ left: model.left, top: model.top });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moveShapeHub.server.updateModel(shapeModel);
                        }
                    });
                });
            });
    </script>
        
        <div id="shape" />
    </body>
    </html>
    
  5. В Обозреватель решений разверните узел Скрипты.

    Библиотеки скриптов для jQuery и SignalR отображаются в проекте.

    Важно!

    Диспетчер пакетов устанавливает более позднюю версию скриптов SignalR.

  6. Обновите ссылки на скрипты в блоке кода, чтобы они соответствовали версиям файлов скриптов в проекте.

Этот код HTML и JavaScript создает красный div объект с именем shape. Он включает поведение перетаскивания фигуры с помощью библиотеки jQuery и использует drag событие для отправки позиции фигуры на сервер.

Запуск приложения

Вы можете запустить приложение, чтобы оно работало. При перетаскивании фигуры вокруг окна браузера она перемещается и в других браузерах.

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

    Снимок экрана: пользователь включает режим отладки и выбирает воспроизведение.

    Откроется окно браузера с красной фигурой в правом верхнем углу.

  2. Скопируйте URL-адрес страницы.

  3. Откройте другой браузер и вставьте URL-адрес в адресную строку.

  4. Перетащите фигуру в одном из окон браузера. Фигура в другом окне браузера будет следующей.

Хотя приложение работает с помощью этого метода, это не рекомендуемая модель программирования. Нет верхнего предела для количества отправляемых сообщений. В результате клиенты и сервер перегружены сообщениями, а производительность снижается. Кроме того, приложение отображает несвязанную анимацию на клиенте. Эта рывковая анимация происходит потому, что фигура мгновенно перемещается каждым методом. Лучше, если фигура плавно перемещается в каждое новое место. Далее вы узнаете, как устранить эти проблемы.

Добавление цикла клиента

Отправка расположения фигуры при каждом событии перемещения мыши создает ненужный объем сетевого трафика. Приложение должно регулировать сообщения от клиента.

Используйте функцию javascript setInterval для настройки цикла, который отправляет на сервер новые сведения о положении с фиксированной скоростью. Этот цикл является базовым представлением "игрового цикла". Это многократно называемая функция, которая управляет всеми функциями игры.

  1. Замените код клиента в файлеDefault.html следующим кодом:

    <!DOCTYPE html>
    <html>
    <head>
    <title>SignalR MoveShape Demo</title>
    <style>
        #shape {
            width: 100px;
            height: 100px;
            background-color: #FF0000;
        }
    </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
        $(function () {
            var moveShapeHub = $.connection.moveShapeHub,
                $shape = $("#shape"),
                // Send a maximum of 10 messages per second 
                // (mouse movements trigger a lot of messages)
                messageFrequency = 10, 
                // Determine how often to send messages in
                // time to abide by the messageFrequency
                updateRate = 1000 / messageFrequency, 
                shapeModel = {
                    left: 0,
                    top: 0
                },
                moved = false;
            moveShapeHub.client.updateShape = function (model) {
                shapeModel = model;
                $shape.css({ left: model.left, top: model.top });
            };
            $.connection.hub.start().done(function () {
                $shape.draggable({
                    drag: function () {
                        shapeModel = $shape.offset();
                        moved = true;
                    }
                });
                // Start the client side server update interval
                setInterval(updateServerModel, updateRate);
            });
            function updateServerModel() {
                // Only update server if we have a new movement
                if (moved) {
                    moveShapeHub.server.updateModel(shapeModel);
                    moved = false;
                }
            }
        });
    </script>
       
    <div id="shape" />
    </body>
    </html>
    

    Важно!

    Необходимо снова заменить ссылки на скрипты. Они должны соответствовать версиям скриптов в проекте.

    Этот новый код добавляет функцию updateServerModel . Он вызывается с фиксированной частотой. Функция отправляет данные о позиции на сервер всякий раз, moved когда флаг указывает, что есть новые данные о позиции для отправки.

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

  3. Скопируйте URL-адрес страницы.

  4. Откройте другой браузер и вставьте URL-адрес в адресную строку.

  5. Перетащите фигуру в одном из окон браузера. Фигура в другом окне браузера будет следующей.

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

Добавление цикла сервера

В текущем приложении сообщения, отправляемые с сервера клиенту, выходят так же часто, как и были получены. Этот сетевой трафик представляет собой аналогичную проблему, как и на клиенте.

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

  1. Замените содержимое MoveShapeHub.cs на следующий код:

    using System;
    using System.Threading;
    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class Broadcaster
        {
            private readonly static Lazy<Broadcaster> _instance = 
                new Lazy<Broadcaster>(() => new Broadcaster());
            // We're going to broadcast to all clients a maximum of 25 times per second
            private readonly TimeSpan BroadcastInterval = 
                TimeSpan.FromMilliseconds(40); 
            private readonly IHubContext _hubContext;
            private Timer _broadcastLoop;
            private ShapeModel _model;
            private bool _modelUpdated;
            public Broadcaster()
            {
                // Save our hub context so we can easily use it 
                // to send to its connected clients
                _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
                _model = new ShapeModel();
                _modelUpdated = false;
                // Start the broadcast loop
                _broadcastLoop = new Timer(
                    BroadcastShape, 
                    null, 
                    BroadcastInterval, 
                    BroadcastInterval);
            }
            public void BroadcastShape(object state)
            {
                // No need to send anything if our model hasn't changed
                if (_modelUpdated)
                {
                    // This is how we can access the Clients property 
                    // in a static hub method or outside of the hub entirely
                    _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
                    _modelUpdated = false;
                }
            }
            public void UpdateShape(ShapeModel clientModel)
            {
                _model = clientModel;
                _modelUpdated = true;
            }
            public static Broadcaster Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
        }
            
        public class MoveShapeHub : Hub
        {
            // Is set via the constructor on each creation
            private Broadcaster _broadcaster;
            public MoveShapeHub()
                : this(Broadcaster.Instance)
            {
            }
            public MoveShapeHub(Broadcaster broadcaster)
            {
                _broadcaster = broadcaster;
            }
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                _broadcaster.UpdateShape(clientModel);
            }
        }
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
            [JsonProperty("top")]
            public double Top { get; set; }
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
        
    }
    
  2. Нажмите кнопку воспроизведения, чтобы запустить приложение.

  3. Скопируйте URL-адрес страницы.

  4. Откройте другой браузер и вставьте URL-адрес в адресную строку.

  5. Перетащите фигуру в одном из окон браузера.

Этот код расширяет клиент для добавления Broadcaster класса . Новый класс регулирует исходящие сообщения с помощью Timer класса из платформы .NET Framework.

Приятно узнать, что сам концентратор является временным. Он создается каждый раз, когда это необходимо. Поэтому приложение создает как одноэлементный Broadcaster объект. Он использует отложенную инициализацию, чтобы отложить Broadcasterсоздание до тех пор, пока оно не потребуется. Это гарантирует, что приложение полностью создаст первый экземпляр концентратора перед запуском таймера.

Затем вызов функции клиента UpdateShape перемещается из метода концентратора UpdateModel . Он больше не вызывается сразу, когда приложение получает входящие сообщения. Вместо этого приложение отправляет сообщения клиентам со скоростью 25 вызовов в секунду. Процессом _broadcastLoop управляет таймер из Broadcaster класса .

Наконец, вместо вызова метода клиента из концентратора Broadcaster напрямую класс должен получить ссылку на текущий работающий _hubContext концентратор. Он получает ссылку с помощью GlobalHost.

Добавление плавной анимации

Приложение почти готово, но мы могли бы сделать еще одно улучшение. Приложение перемещает фигуру на клиенте в ответ на сообщения сервера. Вместо того, чтобы задать новое расположение фигуры, заданное сервером, используйте функцию библиотеки animate пользовательского интерфейса JQuery. Он может плавно перемещать фигуру между ее текущим и новым положением.

  1. Обновите метод клиента updateShape в файлеDefault.html , чтобы он выглядел как выделенный код:

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
            $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                    $shape = $("#shape"),
                    // Send a maximum of 10 messages per second 
                    // (mouse movements trigger a lot of messages)
                    messageFrequency = 10, 
                    // Determine how often to send messages in
                    // time to abide by the messageFrequency
                    updateRate = 1000 / messageFrequency, 
                    shapeModel = {
                        left: 0,
                        top: 0
                    },
                    moved = false;
                moveShapeHub.client.updateShape = function (model) {
                     shapeModel = model;
                     // Gradually move the shape towards the new location (interpolate)
                     // The updateRate is used as the duration because by the time 
                     // we get to the next location we want to be at the "last" location
                     // We also clear the animation queue so that we start a new 
                     // animation and don't lag behind.
                     $shape.animate(shapeModel, { duration: updateRate, queue: false });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moved = true;
                        }
                    });
                    // Start the client side server update interval
                    setInterval(updateServerModel, updateRate);
                });
                function updateServerModel() {
                    // Only update server if we have a new movement
                    if (moved) {
                        moveShapeHub.server.updateModel(shapeModel);
                        moved = false;
                    }
                }
            });
    </script>
       
        <div id="shape" />
    </body>
    </html>
    
  2. Нажмите кнопку воспроизведения, чтобы запустить приложение.

  3. Скопируйте URL-адрес страницы.

  4. Откройте другой браузер и вставьте URL-адрес в адресную строку.

  5. Перетащите фигуру в одном из окон браузера.

Перемещение фигуры в другом окне выглядит менее отрывистым. Приложение интерполирует свое перемещение с течением времени, а не задается один раз для каждого входящего сообщения.

Этот код перемещает фигуру из старого расположения в новое. Сервер задает положение фигуры в течение интервала анимации. В данном случае это 100 миллисекунда. Приложение очищает все предыдущие анимации, запущенные на фигуре, перед запуском новой анимации.

Получите код

Скачивание завершенного проекта

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

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

Дополнительные сведения о SignalR см. в следующих ресурсах:

Дальнейшие действия

Изучив это руководство, вы:

  • Настройка проекта
  • Создание базового приложения
  • Сопоставляются с центром при запуске приложения
  • Добавлен клиент
  • Запущено приложение
  • Добавлен цикл клиента.
  • Добавлен цикл сервера.
  • Добавлена плавная анимация

Перейдите к следующей статье, чтобы узнать, как создать веб-приложение, которое использует ASP.NET SignalR 2 для предоставления функций широковещательной трансляции сервера.