Vysokofrekvenční reálný čas s knihovnou SignalR 1.x

Patrick Fletcher

Upozornění

Tato dokumentace není určená pro nejnovější verzi služby SignalR. Podívejte se na ASP.NET Core SignalR.

V tomto kurzu se dozvíte, jak vytvořit webovou aplikaci, která používá ASP.NET SignalR k poskytování funkcí vysokofrekvenčního zasílání zpráv. Vysokofrekvenční zasílání zpráv v tomto případě znamená aktualizace, které se odesílají pevnou rychlostí; v případě této aplikace až 10 zpráv za sekundu.

Aplikace, kterou vytvoříte v tomto kurzu, zobrazuje obrazec, který uživatelé můžou přetáhnout. Pozice obrazce ve všech ostatních připojených prohlížečích se pak aktualizuje tak, aby odpovídala pozici přetaženého obrazce pomocí časových aktualizací.

Koncepty představené v tomto kurzu mají aplikace pro hraní her v reálném čase a další simulační aplikace.

Komentáře ke kurzu jsou vítány. Pokud máte dotazy, které nesouvisejí přímo s kurzem, můžete je publikovat na fóru ASP.NET SignalR nebo StackOverflow.com.

Přehled

Tento kurz ukazuje, jak vytvořit aplikaci, která v reálném čase sdílí stav objektu s jinými prohlížeči. Aplikace, která vytvoříme, se nazývá MoveShape. Stránka MoveShape zobrazí html Div element, který uživatel může přetáhnout; když uživatel přetáhne div, jeho nová pozice se odešle na server, který pak řekne všem ostatním připojeným klientům, aby aktualizovali pozici obrazce tak, aby odpovídala.

Snímek obrazovky zobrazující stránku aplikace MoveShape

Aplikace vytvořená v tomto kurzu je založená na ukázce, kterou vytvořil Damian Edwards. Video obsahující tuto ukázku najdete tady.

Kurz začne tím, že předvádí, jak odesílat zprávy SignalR z každé události, která se aktivuje při přetažení obrazce. Každý připojený klient pak při každém přijetí zprávy aktualizuje pozici místní verze obrazce.

I když aplikace bude fungovat pomocí této metody, nejedná se o doporučený programovací model, protože počet odesílaných zpráv by nebyl omezen, takže klienti a server by mohli být zahlceni zprávami a výkon by se mohl snížit. Animace zobrazená na klientovi by také byla oddělená, protože obrazec by se okamžitě přesunul každou metodou, místo aby se plynule přesunul na každé nové umístění. V dalších částech kurzu si ukážeme, jak vytvořit funkci časovače, která omezuje maximální rychlost odesílání zpráv klientem nebo serverem a jak hladce přesouvat obrazec mezi umístěními. Konečnou verzi aplikace vytvořené v tomto kurzu si můžete stáhnout z Galerie kódů.

Tento kurz obsahuje následující části:

Požadavky

Tento kurz vyžaduje Visual Studio 2012 nebo Visual Studio 2010. Pokud se použije Visual Studio 2010, projekt bude místo rozhraní .NET Framework 4.5 používat .NET Framework 4.

Pokud používáte Visual Studio 2012, doporučujeme nainstalovat aktualizaci ASP.NET and Web Tools 2012.2. Tato aktualizace obsahuje nové funkce, jako jsou vylepšení publikování, nové funkce a nové šablony.

Pokud máte Visual Studio 2010, ujistěte se, že je nainstalovaný NuGet .

Vytvoření projektu

V této části vytvoříme projekt v sadě Visual Studio.

  1. V nabídce Soubor klikněte na Nový projekt.

  2. V dialogovém okně Nový projekt rozbalte C# v části Šablony a vyberte Web.

  3. Vyberte šablonu ASP.NET Prázdná webová aplikace , pojmenujte projekt MoveShapeDemo a klikněte na OK.

    Vytvoření nového projektu

Přidání balíčků NuGet SignalR a JQuery.UI

Funkci SignalR můžete do projektu přidat instalací balíčku NuGet. V tomto kurzu se také použije balíček JQuery.UI, který umožní přetahování a animaci obrazce.

  1. Klikněte na Nástroje | Správce balíčků NuGet | Konzola Správce balíčků.

  2. Ve správci balíčků zadejte následující příkaz.

    Install-Package Microsoft.AspNet.SignalR -Version 1.1.3
    

    Balíček SignalR nainstaluje řadu dalších balíčků NuGet jako závislostí. Po dokončení instalace budete mít všechny součásti serveru a klienta potřebné k používání služby SignalR v ASP.NET aplikaci.

  3. Zadáním následujícího příkazu do konzoly správce balíčků nainstalujte balíčky JQuery a JQuery.UI.

    Install-Package jQuery.ui.combined
    

Vytvoření základní aplikace

V této části vytvoříme aplikaci prohlížeče, která při každé události přesunutí myši odešle na server umístění obrazce. Server pak tyto informace při přijetí rozesílá všem ostatním připojeným klientům. Tuto aplikaci rozbalíme v dalších částech.

  1. V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Přidat, Třída.... Pojmenujte třídu MoveShapeHub a klikněte na Přidat.

  2. Nahraďte kód v nové třídě MoveShapeHub následujícím kódem.

    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; }
        }
    }
    

    Výše MoveShapeHub uvedená třída je implementací centra SignalR. Stejně jako v Začínáme s kurzem SignalR má centrum metodu, kterou budou klienti volat přímo. V takovém případě klient odešle na server objekt obsahující nové souřadnice X a Y obrazce, který se pak vysílá všem ostatním připojeným klientům. SignalR tento objekt automaticky serializuje pomocí formátu JSON.

    Objekt, který bude odeslán klientovi (ShapeModel), obsahuje členy pro uložení pozice obrazce. Verze objektu na serveru obsahuje také člena, který sleduje, která data klienta se ukládají, aby se danému klientovi neodesílala vlastní data. Tento člen používá JsonIgnore atribut k tomu, aby byla serializována a odeslána klientovi.

  3. V dalším kroku nastavíme centrum při spuštění aplikace. V Průzkumník řešení klikněte pravým tlačítkem na projekt a pak klikněte na Přidat | Třída globální aplikace. Přijměte výchozí název Global a klikněte na OK.

    Přidat globální třídu aplikace

  4. Za poskytnuté příkazy using ve třídě Global.asax.cs přidejte následující using příkaz.

    using System.Web.Routing;
    
  5. Přidejte následující řádek kódu do Application_Start metody třídy Global pro registraci výchozí trasy pro SignalR.

    RouteTable.Routes.MapHubs();
    

    Soubor global.asax by měl vypadat takto:

    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. V dalším kroku přidáme klienta . V Průzkumník řešení klikněte pravým tlačítkem na projekt a pak klikněte na Přidat | Nová položka. V dialogovém okně Přidat novou položku vyberte Stránka HTML. Dejte stránce odpovídající název (například Default.html) a klikněte na Přidat.

  7. V Průzkumník řešení klikněte pravým tlačítkem na stránku, kterou jste právě vytvořili, a klikněte na Nastavit jako úvodní stránku.

  8. Nahraďte výchozí kód na stránce HTML následujícím fragmentem kódu.

    Poznámka

    Ověřte, že níže uvedené odkazy na skripty odpovídají balíčkům přidaných do projektu ve složce Scripts. V sadě Visual Studio 2010 se verze JQuery a SignalR přidané do projektu nemusí shodovat s následujícími čísly verzí.

    <!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>
    

    Výše uvedený kód HTML a JavaScript vytvoří červený div s názvem Shape, povolí chování při přetahování obrazce pomocí knihovny jQuery a použije událost obrazce drag k odeslání pozice obrazce na server.

  9. Spusťte aplikaci stisknutím klávesy F5. Zkopírujte adresu URL stránky a vložte ji do druhého okna prohlížeče. Přetáhněte obrazec v jednom z oken prohlížeče; obrazec v druhém okně prohlížeče by se měl přesunout.

    Snímek obrazovky znázorňující, jak se obrazec, který přetáhnete v jednom okně prohlížeče, přesune v jiném okně

Přidání smyčky klienta

Vzhledem k tomu, že odeslání umístění obrazce při každé události přesunutí myši způsobí zbytečný síťový provoz, je potřeba zprávy z klienta omezovat. Pomocí funkce javascriptu setInterval nastavíme smyčku, která odesílá informace o nové pozici na server za pevnou sazbu. Tato smyčka je velmi základní reprezentací "herní smyčky", opakovaně volané funkce, která řídí všechny funkce hry nebo jiné simulace.

  1. Aktualizujte kód klienta na stránce HTML tak, aby odpovídal následujícímu fragmentu kódu.

    <!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>
    

    Výše uvedená aktualizace přidá updateServerModel funkci, která se volá na pevné frekvenci. Tato funkce odesílá data o poloze na server vždy, když moved příznak indikuje, že je potřeba odeslat data o nové pozici.

  2. Spusťte aplikaci stisknutím klávesy F5. Zkopírujte adresu URL stránky a vložte ji do druhého okna prohlížeče. Přetáhněte obrazec v jednom z oken prohlížeče; obrazec v druhém okně prohlížeče by se měl přesunout. Vzhledem k tomu, že počet zpráv odesílaných na server bude omezený, animace se nezobrazí tak hladce jako v předchozí části.

    Snímek obrazovky znázorňující, jak se obrazec, který přetáhnete v jednom okně prohlížeče, přesune v jiném okně, když přidáte smyčku klienta

Přidání smyčky serveru

V aktuální aplikaci se zprávy odesílané ze serveru klientovi zobrazují tak často, jak jsou přijímány. To představuje podobný problém, jako byl zaznamenán na klientovi; zprávy se můžou posílat častěji, než je potřeba, a v důsledku toho se připojení může zahltit. Tato část popisuje, jak aktualizovat server tak, aby implementovali časovač, který omezuje rychlost odchozích zpráv.

  1. Obsah MoveShapeHub.cs nahraďte následujícím fragmentem kódu.

    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; }
        }
        
    }
    

    Výše uvedený kód rozbalí klienta a přidá Broadcaster třídu, která omezí odchozí zprávy pomocí Timer třídy z rozhraní .NET Framework.

    Vzhledem k tomu, že samotné centrum je přechodné (vytváří se pokaždé, když je potřeba), Broadcaster vytvoří se jako jednoúčelový. Opožděná inicializace (zavedená v rozhraní .NET 4) se používá k odložení jejího vytvoření na dobu, kdy je potřeba, a zajišťuje tak, aby se před spuštěním časovače úplně vytvořila první instance centra.

    Volání funkce klientů UpdateShape se pak přesune mimo metodu centra UpdateModel , takže se už nevolá okamžitě při přijetí příchozích zpráv. Místo toho se zprávy klientům odesílají rychlostí 25 volání za sekundu spravované časovačem _broadcastLoop z třídy Broadcaster .

    A konečně, místo přímého volání klientské metody z centra Broadcaster musí třída získat odkaz na aktuálně provozní centrum (_hubContext) pomocí GlobalHost.

  2. Spusťte aplikaci stisknutím klávesy F5. Zkopírujte adresu URL stránky a vložte ji do druhého okna prohlížeče. Přetáhněte obrazec v jednom z oken prohlížeče; obrazec v druhém okně prohlížeče by se měl přesunout. V prohlížeči nebude viditelný rozdíl oproti předchozí části, ale omezí se počet zpráv odeslaných klientovi.

    Snímek obrazovky znázorňující, jak se obrazec, který přetáhnete v jednom okně prohlížeče, přesune v jiném okně, když přidáte smyčku serveru

Přidání plynulé animace do klienta

Aplikace je téměř hotová, ale mohli bychom udělat ještě jedno vylepšení, a to v pohybu obrazce na klientovi, který se přesouvá v reakci na zprávy na serveru. Místo toho, abychom nastavili umístění obrazce na nové umístění zadané serverem, použijeme funkci knihovny animate uživatelského rozhraní JQuery k plynulému přesunu obrazce mezi aktuální a novou pozicí.

  1. Aktualizujte metodu klienta tak, aby vypadala updateShape jako zvýrazněný kód níže:

    <!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>
    

    Výše uvedený kód přesune obrazec ze starého umístění do nového obrazce zadaného serverem v průběhu intervalu animace (v tomto případě 100 milisekund). Všechny předchozí animace spuštěné na obrazci se před zahájením nové animace vymažou.

  2. Spusťte aplikaci stisknutím klávesy F5. Zkopírujte adresu URL stránky a vložte ji do druhého okna prohlížeče. Přetáhněte obrazec v jednom z oken prohlížeče; obrazec v druhém okně prohlížeče by se měl přesunout. Pohyb obrazce v druhém okně by měl být méně trhaný, protože jeho pohyb je interpolován v průběhu času, místo aby se nastavoval jednou pro příchozí zprávu.

    Snímek obrazovky znázorňující, jak se obrazec, který přetáhnete v jednom okně prohlížeče, pohybuje v jiném okně, když do klienta přidáte hladkou animaci.

Další kroky

V tomto kurzu jste se naučili naprogramovat aplikaci SignalR, která odesílá vysokofrekvenční zprávy mezi klienty a servery. Toto komunikační paradigma je užitečné pro vývoj online her a dalších simulací, jako je například hra ShootR vytvořená pomocí SignalR.

Kompletní aplikaci vytvořenou v tomto kurzu si můžete stáhnout z Galerie kódů.

Další informace o konceptech vývoje služby SignalR najdete na následujících webech se zdrojovým kódem a zdroji prostředků SignalR: