Udostępnij za pośrednictwem


Wysyłanie w czasie rzeczywistym z wysoką częstotliwością przy użyciu usługi SignalR 1.x

Autor : Patrick Fletcher

Ostrzeżenie

Ta dokumentacja nie jest przeznaczona dla najnowszej wersji usługi SignalR. Przyjrzyj się ASP.NET Core SignalR.

W tym samouczku pokazano, jak utworzyć aplikację internetową, która używa ASP.NET SignalR do zapewnienia funkcji obsługi komunikatów o wysokiej częstotliwości. Obsługa komunikatów o wysokiej częstotliwości w tym przypadku oznacza aktualizacje, które są wysyłane ze stałą szybkością; w przypadku tej aplikacji maksymalnie 10 komunikatów na sekundę.

Aplikacja utworzona w tym samouczku wyświetla kształt, który użytkownicy mogą przeciągać. Pozycja kształtu we wszystkich innych połączonych przeglądarkach zostanie zaktualizowana tak, aby odpowiadała położeniu przeciąganego kształtu przy użyciu aktualizacji czasowych.

Pojęcia wprowadzone w tym samouczku zawierają aplikacje w grach w czasie rzeczywistym i innych aplikacjach symulacji.

Komentarze do samouczka są mile widziane. Jeśli masz pytania, które nie są bezpośrednio związane z samouczkiem, możesz opublikować je na forum ASP.NET SignalR lub StackOverflow.com.

Omówienie

W tym samouczku pokazano, jak utworzyć aplikację, która udostępnia stan obiektu innym przeglądarkom w czasie rzeczywistym. Utworzona aplikacja nosi nazwę MoveShape. Na stronie MoveShape zostanie wyświetlony element Div HTML, który użytkownik może przeciągnąć; gdy użytkownik przeciągnie element Div, jego nowe położenie zostanie wysłane na serwer, co poinformuje wszystkich innych połączonych klientów o zaktualizowanie pozycji kształtu do dopasowania.

Zrzut ekranu przedstawiający stronę aplikacji MoveShape.

Aplikacja utworzona w tym samouczku jest oparta na pokazie Damiana Edwardsa. Wideo zawierające ten pokaz można zobaczyć tutaj.

Samouczek rozpocznie się od zademonstrowania sposobu wysyłania komunikatów usługi SignalR z każdego zdarzenia, które jest wyzwalane w miarę przeciągania kształtu. Każdy połączony klient zaktualizuje pozycję lokalnego wersji kształtu za każdym razem, gdy zostanie odebrany komunikat.

Chociaż aplikacja będzie działać przy użyciu tej metody, nie jest to zalecany model programowania, ponieważ nie będzie górnego limitu liczby wysyłanych komunikatów, więc klienci i serwer mogą zostać przeciążeni komunikatami i wydajnością. Wyświetlana animacja na kliencie będzie również rozłączona, ponieważ kształt zostanie natychmiast przeniesiony przez każdą metodę, zamiast bezproblemowo przenosić się do każdej nowej lokalizacji. W kolejnych sekcjach samouczka pokazano, jak utworzyć funkcję czasomierza, która ogranicza maksymalną szybkość wysyłania komunikatów przez klienta lub serwer oraz sposób bezproblemowego przenoszenia kształtu między lokalizacjami. Ostateczna wersja aplikacji utworzonej w tym samouczku można pobrać z galerii kodu.

Ten samouczek zawiera następujące sekcje:

Wymagania wstępne

Ten samouczek wymaga programu Visual Studio 2012 lub Visual Studio 2010. Jeśli jest używany program Visual Studio 2010, projekt będzie używać .NET Framework 4, a nie .NET Framework 4.5.

Jeśli używasz programu Visual Studio 2012, zaleca się zainstalowanie aktualizacji ASP.NET and Web Tools 2012.2. Ta aktualizacja zawiera nowe funkcje, takie jak ulepszenia publikowania, nowe funkcje i nowe szablony.

Jeśli masz program Visual Studio 2010, upewnij się, że jest zainstalowany pakiet NuGet .

Tworzenie projektu

W tej sekcji utworzymy projekt w programie Visual Studio.

  1. W menu Plik kliknij pozycję Nowy projekt.

  2. W oknie dialogowym Nowy projekt rozwiń węzeł C# w obszarze Szablony i wybierz pozycję Sieć Web.

  3. Wybierz szablon pustej aplikacji internetowej ASP.NET , nadaj projektowi nazwę MoveShapeDemo, a następnie kliknij przycisk OK.

    Tworzenie nowego projektu

Dodawanie pakietów NuGet usługi SignalR i JQuery.UI

Funkcje usługi SignalR można dodać do projektu, instalując pakiet NuGet. Ten samouczek będzie również używać pakietu JQuery.UI, aby umożliwić przeciąganie i animowanie kształtu.

  1. Kliknij pozycję Narzędzia | Menedżer pakietów NuGet | Konsola Menedżera pakietów.

  2. Wprowadź następujące polecenie w Menedżerze pakietów.

    Install-Package Microsoft.AspNet.SignalR -Version 1.1.3
    

    Pakiet SignalR instaluje wiele innych pakietów NuGet jako zależności. Po zakończeniu instalacji wszystkie składniki serwera i klienta wymagane do korzystania z usługi SignalR w aplikacji ASP.NET.

  3. Wprowadź następujące polecenie w konsoli menedżera pakietów, aby zainstalować pakiety JQuery i JQuery.UI.

    Install-Package jQuery.ui.combined
    

Tworzenie aplikacji podstawowej

W tej sekcji utworzymy aplikację przeglądarki, która wysyła lokalizację kształtu na serwer podczas każdego zdarzenia przenoszenia myszy. Następnie serwer emituje te informacje do wszystkich innych połączonych klientów w miarę odbierania. Rozszerzymy tę aplikację w kolejnych sekcjach.

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt i wybierz pozycję Dodaj, Klasa.... Nadaj klasie nazwę MoveShapeHub i kliknij przycisk Dodaj.

  2. Zastąp kod w nowej klasie MoveShapeHub następującym kodem.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    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; }
        }
    }
    

    Powyżej MoveShapeHub klasa jest implementacją koncentratora SignalR. Podobnie jak w Wprowadzenie z samouczkiem usługi SignalR, centrum ma metodę, którą klienci będą wywoływać bezpośrednio. W takim przypadku klient wyśle obiekt zawierający nowe współrzędne X i Y kształtu do serwera, który następnie zostanie wyemitowany do wszystkich innych połączonych klientów. Usługa SignalR automatycznie serializuje ten obiekt przy użyciu formatu JSON.

    Obiekt, który zostanie wysłany do klienta (ShapeModel) zawiera elementy członkowskie do przechowywania położenia kształtu. Wersja obiektu na serwerze zawiera również element członkowski do śledzenia, które dane klienta są przechowywane, dzięki czemu dany klient nie będzie wysyłał własnych danych. Ten element członkowski używa atrybutu JsonIgnore , aby zachować jego serializacji i wysyłane do klienta.

  3. Następnie skonfigurujemy centrum po uruchomieniu aplikacji. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt, a następnie kliknij przycisk Dodaj | Globalna klasa aplikacji. Zaakceptuj domyślną nazwę globalnych i kliknij przycisk OK.

    Dodawanie globalnej klasy aplikacji

  4. Dodaj następującą using instrukcję po podanych instrukcjach using w klasie Global.asax.cs.

    using System.Web.Routing;
    
  5. Dodaj następujący wiersz kodu w metodzie klasy Global, Application_Start aby zarejestrować trasę domyślną dla usługi SignalR.

    RouteTable.Routes.MapHubs();
    

    Plik global.asax powinien wyglądać następująco:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.SessionState;
    
    using System.Web.Routing;
    
    namespace MoveShapeDemo
    {
        public class Global : System.Web.HttpApplication
        {
            protected void Application_Start(object sender, EventArgs e)
            {
                RouteTable.Routes.MapHubs();
            }
        }
    }
    
  6. Następnie dodamy klienta. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt, a następnie kliknij przycisk Dodaj | Nowy element. W oknie dialogowym Dodawanie nowego elementu wybierz pozycję Strona HTML. Nadaj stronie odpowiednią nazwę (na przykład Default.html) i kliknij przycisk Dodaj.

  7. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy utworzoną stronę, a następnie kliknij pozycję Ustaw jako stronę startową.

  8. Zastąp domyślny kod na stronie HTML poniższym fragmentem kodu.

    Uwaga

    Sprawdź, czy odwołania do skryptu poniżej są zgodne z pakietami dodanymi do projektu w folderze Scripts. W programie Visual Studio 2010 wersja biblioteki JQuery i SignalR dodana do projektu może nie być zgodna z poniższymi numerami wersji.

    <!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.6.4.js"></script>
    <script src="Scripts/jquery-ui-1.10.2.js"></script>
    <script src="Scripts/jquery.signalR-1.0.1.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>
    

    Powyższy kod HTML i JavaScript tworzy czerwony div o nazwie Shape, umożliwia przeciąganie kształtu przy użyciu biblioteki jQuery i używa zdarzenia kształtu do wysłania pozycji kształtu drag na serwer.

  9. Uruchom aplikację, naciskając klawisz F5. Skopiuj adres URL strony i wklej go w drugim oknie przeglądarki. Przeciągnij kształt w jednym z okien przeglądarki; kształt w innym oknie przeglądarki powinien zostać przeniesiony.

    Zrzut ekranu przedstawiający sposób przeciągania kształtu w jednym oknie przeglądarki w innym oknie.

Dodawanie pętli klienta

Ponieważ wysłanie lokalizacji kształtu na każdym zdarzeniu przenoszenia myszy spowoduje utworzenie niepotrzebnej ilości ruchu sieciowego, komunikaty z klienta muszą zostać ograniczone. Użyjemy funkcji javascript setInterval , aby skonfigurować pętlę, która wysyła nowe informacje o pozycji do serwera z stałą szybkością. Ta pętla jest bardzo podstawową reprezentacją "pętli gier", wielokrotnie nazywaną funkcją, która napędza wszystkie funkcje gry lub innej symulacji.

  1. Zaktualizuj kod klienta na stronie HTML, aby był zgodny z poniższym fragmentem kodu.

    <!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.6.4.js"></script>
    <script src="Scripts/jquery-ui-1.10.2.js"></script>
    <script src="Scripts/jquery.signalR-1.0.1.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>
    

    W powyższej aktualizacji dodano updateServerModel funkcję, która jest wywoływana z stałą częstotliwością. Ta funkcja wysyła dane pozycji do serwera za każdym razem, gdy moved flaga wskazuje, że do wysłania są nowe dane pozycji.

  2. Uruchom aplikację, naciskając klawisz F5. Skopiuj adres URL strony i wklej go w drugim oknie przeglądarki. Przeciągnij kształt w jednym z okien przeglądarki; kształt w innym oknie przeglądarki powinien zostać przeniesiony. Ponieważ liczba komunikatów wysyłanych do serwera zostanie ograniczona, animacja nie będzie wyświetlana tak gładko, jak w poprzedniej sekcji.

    Zrzut ekranu przedstawiający sposób przeciągania kształtu w jednym oknie przeglądarki podczas dodawania pętli klienta.

Dodawanie pętli serwera

W bieżącej aplikacji komunikaty wysyłane z serwera do klienta są wysyłane tak często, jak są odbierane. Stanowi to podobny problem, jak pokazano na kliencie; komunikaty mogą być wysyłane częściej niż są potrzebne, a połączenie może zostać zalane w wyniku. W tej sekcji opisano sposób aktualizowania serwera w celu zaimplementowania czasomierza, który ogranicza szybkość komunikatów wychodzących.

  1. Zastąp zawartość MoveShapeHub.cs pliku poniższym fragmentem kodu.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    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; }
        }
        
    }
    

    Powyższy kod rozszerza klienta, aby dodać klasę Broadcaster , która ogranicza komunikaty wychodzące przy użyciu Timer klasy z programu .NET Framework.

    Ponieważ sama piasta jest przechodnia (jest tworzona za każdym razem, gdy jest potrzebna), Broadcaster obiekt zostanie utworzony jako pojedynczy. Inicjalizacja z opóźnieniem (wprowadzona na platformie .NET 4) służy do odroczenia tworzenia, dopóki nie będzie potrzebna, zapewniając, że pierwsze wystąpienie centrum zostało całkowicie utworzone przed uruchomieniem czasomierza.

    Wywołanie funkcji klienta UpdateShape jest następnie przenoszone z metody centrum UpdateModel , aby nie było już wywoływane natychmiast po odebraniu komunikatów przychodzących. Zamiast tego komunikaty do klientów będą wysyłane w tempie 25 wywołań na sekundę zarządzanych przez _broadcastLoop czasomierz z poziomu Broadcaster klasy.

    Na koniec zamiast wywoływać metodę klienta bezpośrednio z centrum, Broadcaster klasa musi uzyskać odwołanie do aktualnie działającego centrum (_hubContext) przy użyciu klasy GlobalHost.

  2. Uruchom aplikację, naciskając klawisz F5. Skopiuj adres URL strony i wklej go w drugim oknie przeglądarki. Przeciągnij kształt w jednym z okien przeglądarki; kształt w innym oknie przeglądarki powinien zostać przeniesiony. Nie będzie widoczna różnica w przeglądarce z poprzedniej sekcji, ale liczba komunikatów wysyłanych do klienta zostanie ograniczona.

    Zrzut ekranu przedstawiający sposób przeciągania kształtu w jednym oknie przeglądarki w innym oknie podczas dodawania pętli serwera.

Dodawanie płynnej animacji na kliencie

Aplikacja jest prawie kompletna, ale możemy wprowadzić jeszcze jedną poprawę w ruchu kształtu na kliencie, ponieważ jest on przenoszony w odpowiedzi na komunikaty serwera. Zamiast ustawiać położenie kształtu na nową lokalizację nadaną przez serwer, użyjemy funkcji biblioteki animate interfejsu użytkownika JQuery, aby płynnie przenosić kształt między jego bieżącym i nowym położeniem.

  1. Zaktualizuj metodę klienta updateShape tak, aby wyglądała jak wyróżniony kod poniżej:

    <!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.6.4.js"></script>
    <script src="Scripts/jquery-ui-1.10.2.js"></script>
    <script src="Scripts/jquery.signalR-1.0.1.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>
    

    Powyższy kod przenosi kształt ze starej lokalizacji do nowej podanej przez serwer w trakcie interwału animacji (w tym przypadku 100 milisekund). Przed rozpoczęciem nowej animacji wszystkie poprzednie animacje uruchomione na kształcie są czyszczone.

  2. Uruchom aplikację, naciskając klawisz F5. Skopiuj adres URL strony i wklej go w drugim oknie przeglądarki. Przeciągnij kształt w jednym z okien przeglądarki; kształt w innym oknie przeglądarki powinien zostać przeniesiony. Ruch kształtu w innym oknie powinien wyglądać mniej szarpnięć, ponieważ jego ruch jest interpolowany w czasie, a nie ustawiany raz na przychodzący komunikat.

    Zrzut ekranu przedstawiający sposób przeciągania kształtu w jednym oknie przeglądarki podczas dodawania płynnej animacji na kliencie.

Dalsze kroki

W tym samouczku przedstawiono sposób programowania aplikacji SignalR, która wysyła komunikaty o wysokiej częstotliwości między klientami i serwerami. Ten paradygmat komunikacji jest przydatny do tworzenia gier online i innych symulacji, takich jak gra ShootR utworzona za pomocą signalr.

Pełną aplikację utworzoną w tym samouczku można pobrać z galerii kodu.

Aby dowiedzieć się więcej na temat pojęć związanych z programowaniem w usłudze SignalR, odwiedź następujące witryny dotyczące kodu źródłowego i zasobów usługi SignalR: