Partager via


Tutoriel : Diffusion de serveur avec SignalR 2

Avertissement

Cette documentation ne concerne pas la dernière version de SignalR. Jetez un coup d’œil à ASP.NET Core SignalR.

Ce tutoriel montre comment créer une application web qui utilise ASP.NET SignalR 2 pour fournir des fonctionnalités de diffusion de serveur. La diffusion du serveur signifie que le serveur démarre les communications envoyées aux clients.

L’application que vous allez créer dans ce tutoriel simule un ticker boursier, un scénario classique pour la fonctionnalité de diffusion de serveur. Régulièrement, le serveur met à jour de manière aléatoire les cours des actions et diffuse les mises à jour à tous les clients connectés. Dans le navigateur, les nombres et les symboles dans les colonnes Change et % changent dynamiquement en réponse aux notifications du serveur. Si vous ouvrez des navigateurs supplémentaires à la même URL, ils affichent tous les mêmes données et les mêmes modifications apportées aux données simultanément.

Capture d’écran montrant comment plusieurs navigateurs web affichent simultanément les mêmes données mises à jour.

Dans ce tutoriel, vous allez :

  • Créer le projet
  • Configurer le code du serveur
  • Examiner le code du serveur
  • Configurer le code client
  • Examiner le code client
  • Tester l’application
  • Activation de la journalisation

Important

Si vous ne souhaitez pas suivre les étapes de création de l’application, vous pouvez installer le package SignalR.Sample dans un nouveau projet d’application web vide ASP.NET. Si vous installez le package NuGet sans effectuer les étapes de ce didacticiel, vous devez suivre les instructions du fichier readme.txt . Pour exécuter le package, vous devez ajouter une classe de démarrage OWIN qui appelle la ConfigureSignalR méthode dans le package installé. Vous recevrez une erreur si vous n’ajoutez pas la classe de démarrage OWIN. Consultez la section Installer l’exemple StockTicker de cet article.

Prérequis

Créer le projet

Cette section montre comment utiliser Visual Studio 2017 pour créer une application web ASP.NET vide.

  1. Dans Visual Studio, créez une application web ASP.NET.

    Capture d’écran montrant comment créer une application web ASP.NET.

  2. Dans la fenêtre Nouvelle application web ASP.NET - SignalR.StockTicker , laissez Vide sélectionné et sélectionnez OK.

Configurer le code du serveur

Dans cette section, vous allez configurer le code qui s’exécute sur le serveur.

Créer la classe Stock

Vous commencez par créer la classe de modèle Stock que vous utiliserez pour stocker et transmettre des informations sur un stock.

  1. Dans Explorateur de solutions, cliquez avec le bouton droit sur le projet et sélectionnez Ajouter une>classe.

  2. Nommez la classe Stock et ajoutez-la au projet.

  3. Remplacez le code dans le fichier Stock.cs par ce code :

    using System;
    
    namespace SignalR.StockTicker
    {
        public class Stock
        {
            private decimal _price;
    
            public string Symbol { get; set; }
    
            public decimal Price
            {
                get
                {
                    return _price;
                }
                set
                {
                    if (_price == value)
                    {
                        return;
                    }
    
                    _price = value;
    
                    if (DayOpen == 0)
                    {
                        DayOpen = _price;
                    }
                }
            }
    
            public decimal DayOpen { get; private set; }
    
            public decimal Change
            {
                get
                {
                    return Price - DayOpen;
                }
            }
    
            public double PercentChange
            {
                get
                {
                    return (double)Math.Round(Change / Price, 4);
                }
            }
        }
    }
    

    Les deux propriétés que vous définissez lorsque vous créez des actions sont Symbol (par exemple, MSFT pour Microsoft) et Price. Les autres propriétés dépendent de la façon et du moment où vous définissez Price. La première fois que vous définissez Price, la valeur est propagée à DayOpen. Après cela, lorsque vous définissez Price, l’application calcule les valeurs de propriété Change et PercentChange en fonction de la différence entre Price et DayOpen.

Créer les classes StockTickerHub et StockTicker

Vous allez utiliser l’API Hub SignalR pour gérer l’interaction de serveur à client. Une StockTickerHub classe qui dérive de la classe SignalR Hub gère les connexions de réception et les appels de méthode des clients. Vous devez également gérer les données de stock et exécuter un Timer objet. L’objet Timer déclenche régulièrement des mises à jour de prix indépendamment des connexions clientes. Vous ne pouvez pas placer ces fonctions dans une Hub classe, car les hubs sont temporaires. L’application crée une Hub classe instance pour chaque tâche sur le hub, comme les connexions et les appels du client au serveur. Ainsi, le mécanisme qui conserve les données boursières, met à jour les prix et diffuse les mises à jour de prix doit s’exécuter dans une classe distincte. Vous allez nommer la classe StockTicker.

Diffusion à partir de StockTicker

Comme vous souhaitez qu’une seule instance de la StockTicker classe s’exécute sur le serveur, vous devez configurer une référence à partir de chaque StockTickerHub instance à la instance singleton StockTicker . La StockTicker classe doit être diffusée aux clients, car elle contient les données de stock et déclenche des mises à jour, mais StockTicker n’est pas une Hub classe. La StockTicker classe doit obtenir une référence à l’objet de contexte de connexion SignalR Hub. Il peut ensuite utiliser l’objet de contexte de connexion SignalR pour diffuser aux clients.

Créer StockTickerHub.cs

  1. Dans Explorateur de solutions, cliquez avec le bouton droit sur le projet et sélectionnez Ajouter un>nouvel élément.

  2. Dans Ajouter un nouvel élément - SignalR.StockTicker, sélectionnezVisual C#>Web>SignalRinstallé>, puis classe Hub SignalR (v2).

  3. Nommez la classe StockTickerHub et ajoutez-la au projet.

    Cette étape crée le fichier de classe StockTickerHub.cs . Simultanément, il ajoute au projet un ensemble de fichiers de script et de références d’assembly qui prennent en charge SignalR.

  4. Remplacez le code dans le fichier StockTickerHub.cs par ce code :

    using System.Collections.Generic;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    
    namespace SignalR.StockTicker
    {
        [HubName("stockTickerMini")]
        public class StockTickerHub : Hub
        {
            private readonly StockTicker _stockTicker;
    
            public StockTickerHub() : this(StockTicker.Instance) { }
    
            public StockTickerHub(StockTicker stockTicker)
            {
                _stockTicker = stockTicker;
            }
    
            public IEnumerable<Stock> GetAllStocks()
            {
                return _stockTicker.GetAllStocks();
            }
        }
    }
    
  5. Enregistrez le fichier.

L’application utilise la classe Hub pour définir les méthodes que les clients peuvent appeler sur le serveur. Vous définissez une méthode : GetAllStocks(). Lorsqu’un client se connecte initialement au serveur, il appelle cette méthode pour obtenir la liste de toutes les actions avec leurs prix actuels. La méthode peut s’exécuter de manière synchrone et retourner IEnumerable<Stock> , car elle retourne des données à partir de la mémoire.

Si la méthode devait obtenir les données en effectuant quelque chose qui impliquerait l’attente, comme une recherche de base de données ou un appel de service web, vous devez spécifier Task<IEnumerable<Stock>> comme valeur de retour pour activer le traitement asynchrone. Pour plus d’informations, consultez ASP.NET Guide de l’API SignalR Hubs - Serveur - Quand s’exécuter de manière asynchrone.

L’attribut HubName spécifie comment l’application référencera le hub en code JavaScript sur le client. Le nom par défaut sur le client si vous n’utilisez pas cet attribut est une version camelCase du nom de la classe, qui dans ce cas serait stockTickerHub.

Comme vous le verrez plus tard lorsque vous créerez la StockTicker classe, l’application crée un singleton instance de cette classe dans sa propriété statiqueInstance. Ce singleton instance de StockTicker est en mémoire, quel que soit le nombre de clients qui se connectent ou se déconnectent. C’instance est ce que la GetAllStocks() méthode utilise pour retourner les informations boursières actuelles.

Créer StockTicker.cs

  1. Dans Explorateur de solutions, cliquez avec le bouton droit sur le projet et sélectionnez Ajouter une>classe.

  2. Nommez la classe StockTicker et ajoutez-la au projet.

  3. Remplacez le code dans le fichier StockTicker.cs par ce code :

    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Threading;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    
    namespace SignalR.StockTicker
    {
        public class StockTicker
        {
            // Singleton instance
            private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
    
            private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
    
            private readonly object _updateStockPricesLock = new object();
    
            //stock can go up or down by a percentage of this factor on each change
            private readonly double _rangePercent = .002;
    
            private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);
            private readonly Random _updateOrNotRandom = new Random();
    
            private readonly Timer _timer;
            private volatile bool _updatingStockPrices = false;
    
            private StockTicker(IHubConnectionContext<dynamic> clients)
            {
                Clients = clients;
    
                _stocks.Clear();
                var stocks = new List<Stock>
                {
                    new Stock { Symbol = "MSFT", Price = 30.31m },
                    new Stock { Symbol = "APPL", Price = 578.18m },
                    new Stock { Symbol = "GOOG", Price = 570.30m }
                };
                stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));
    
                _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
    
            }
    
            public static StockTicker Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
    
            private IHubConnectionContext<dynamic> Clients
            {
                get;
                set;
            }
    
            public IEnumerable<Stock> GetAllStocks()
            {
                return _stocks.Values;
            }
    
            private void UpdateStockPrices(object state)
            {
                lock (_updateStockPricesLock)
                {
                    if (!_updatingStockPrices)
                    {
                        _updatingStockPrices = true;
    
                        foreach (var stock in _stocks.Values)
                        {
                            if (TryUpdateStockPrice(stock))
                            {
                                BroadcastStockPrice(stock);
                            }
                        }
    
                        _updatingStockPrices = false;
                    }
                }
            }
    
            private bool TryUpdateStockPrice(Stock stock)
            {
                // Randomly choose whether to update this stock or not
                var r = _updateOrNotRandom.NextDouble();
                if (r > .1)
                {
                    return false;
                }
    
                // Update the stock price by a random factor of the range percent
                var random = new Random((int)Math.Floor(stock.Price));
                var percentChange = random.NextDouble() * _rangePercent;
                var pos = random.NextDouble() > .51;
                var change = Math.Round(stock.Price * (decimal)percentChange, 2);
                change = pos ? change : -change;
    
                stock.Price += change;
                return true;
            }
    
            private void BroadcastStockPrice(Stock stock)
            {
                Clients.All.updateStockPrice(stock);
            }
    
        }
    }
    

Étant donné que tous les threads exécutent la même instance du code StockTicker, la classe StockTicker doit être thread-safe.

Examiner le code du serveur

Si vous examinez le code du serveur, cela vous aidera à comprendre le fonctionnement de l’application.

Stockage de la instance singleton dans un champ statique

Le code initialise le champ statique _instance qui sauvegarde la Instance propriété avec un instance de la classe . Étant donné que le constructeur est privé, il s’agit de la seule instance de la classe que l’application peut créer. L’application utilise l’initialisation différée pour le _instance champ. Ce n’est pas pour des raisons de performances. Il s’agit de s’assurer que le instance création est thread-safe.

private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));

public static StockTicker Instance
{
    get
    {
        return _instance.Value;
    }
}

Chaque fois qu’un client se connecte au serveur, un nouveau instance de la classe StockTickerHub s’exécutant dans un thread distinct obtient le singleton StockTicker instance à partir de la StockTicker.Instance propriété statique, comme vous l’avez vu précédemment dans la StockTickerHub classe .

Stockage des données boursières dans un concurrentDictionary

Le constructeur initialise la _stocks collection avec des exemples de données boursières et GetAllStocks retourne les stocks. Comme vous l’avez vu précédemment, cette collection de stocks est retournée par StockTickerHub.GetAllStocks, qui est une méthode serveur dans la Hub classe que les clients peuvent appeler.

private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
private StockTicker(IHubConnectionContext<dynamic> clients)
{
    Clients = clients;

    _stocks.Clear();
    var stocks = new List<Stock>
    {
        new Stock { Symbol = "MSFT", Price = 30.31m },
        new Stock { Symbol = "APPL", Price = 578.18m },
        new Stock { Symbol = "GOOG", Price = 570.30m }
    };
    stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));

    _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
}

public IEnumerable<Stock> GetAllStocks()
{
    return _stocks.Values;
}

La collection d’actions est définie comme un type ConcurrentDictionary pour la sécurité des threads. Vous pouvez également utiliser un objet Dictionary et verrouiller explicitement le dictionnaire lorsque vous y apportez des modifications.

Pour cet exemple d’application, vous pouvez stocker les données d’application en mémoire et perdre les données lorsque l’application supprime les StockTicker instance. Dans une application réelle, vous travaillez avec un magasin de données back-end comme une base de données.

Mise à jour périodique des cours boursiers

Le constructeur démarre un Timer objet qui appelle régulièrement des méthodes qui mettent à jour le cours des actions de manière aléatoire.

_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);

private void UpdateStockPrices(object state)
{
    lock (_updateStockPricesLock)
    {
        if (!_updatingStockPrices)
        {
            _updatingStockPrices = true;

            foreach (var stock in _stocks.Values)
            {
                if (TryUpdateStockPrice(stock))
                {
                    BroadcastStockPrice(stock);
                }
            }

            _updatingStockPrices = false;
        }
    }
}

private bool TryUpdateStockPrice(Stock stock)
{
    // Randomly choose whether to update this stock or not
    var r = _updateOrNotRandom.NextDouble();
    if (r > .1)
    {
        return false;
    }

    // Update the stock price by a random factor of the range percent
    var random = new Random((int)Math.Floor(stock.Price));
    var percentChange = random.NextDouble() * _rangePercent;
    var pos = random.NextDouble() > .51;
    var change = Math.Round(stock.Price * (decimal)percentChange, 2);
    change = pos ? change : -change;

    stock.Price += change;
    return true;
}

Timer appelle UpdateStockPrices, qui passe la valeur null dans le paramètre d’état. Avant de mettre à jour les prix, l’application prend un verrou sur l’objet _updateStockPricesLock . Le code vérifie si un autre thread met déjà à jour les prix, puis appelle TryUpdateStockPrice chaque action de la liste. La TryUpdateStockPrice méthode détermine s’il faut modifier le cours de l’action et combien le modifier. Si le cours de l’action change, l’application appelle BroadcastStockPrice pour diffuser la modification du cours de l’action à tous les clients connectés.

L’indicateur _updatingStockPrices désigné volatile pour s’assurer qu’il est thread-safe.

private volatile bool _updatingStockPrices = false;

Dans une application réelle, la TryUpdateStockPrice méthode appelle un service web pour rechercher le prix. Dans ce code, l’application utilise un générateur de nombres aléatoires pour apporter des modifications aléatoires.

Obtention du contexte SignalR afin que la classe StockTicker puisse diffuser vers les clients

Étant donné que les modifications de prix proviennent ici de l’objet StockTicker , il s’agit de l’objet qui doit appeler une updateStockPrice méthode sur tous les clients connectés. Dans une Hub classe, vous disposez d’une API pour appeler des méthodes clientes, mais StockTicker ne dérive pas de la Hub classe et n’a aucune référence à un Hub objet. Pour diffuser sur des clients connectés, la StockTicker classe doit obtenir le contexte SignalR instance pour la StockTickerHub classe et l’utiliser pour appeler des méthodes sur les clients.

Le code obtient une référence au contexte SignalR lorsqu’il crée la classe singleton instance, passe cette référence au constructeur et que le constructeur la place dans la Clients propriété .

Il existe deux raisons pour lesquelles vous ne souhaitez obtenir le contexte qu’une seule fois : l’obtention du contexte est une tâche coûteuse et l’obtention d’une seule fois permet de s’assurer que l’application conserve l’ordre prévu des messages envoyés aux clients.

private readonly static Lazy<StockTicker> _instance =
    new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));

private StockTicker(IHubConnectionContext<dynamic> clients)
{
    Clients = clients;

    // Remainder of constructor ...
}

private IHubConnectionContext<dynamic> Clients
{
    get;
    set;
}

private void BroadcastStockPrice(Stock stock)
{
    Clients.All.updateStockPrice(stock);
}

L’obtention de la Clients propriété du contexte et son placement dans la propriété vous permettent d’écrire du StockTickerClient code pour appeler des méthodes clientes qui ressemblent à celles d’une Hub classe. Par instance, pour diffuser sur tous les clients, vous pouvez écrire Clients.All.updateStockPrice(stock).

La updateStockPrice méthode que vous appelez BroadcastStockPrice n’existe pas encore. Vous l’ajouterez ultérieurement lorsque vous écrirez du code qui s’exécute sur le client. Vous pouvez faire référence ici updateStockPrice , car Clients.All est dynamique, ce qui signifie que l’application évaluera l’expression au moment de l’exécution. Lorsque cet appel de méthode s’exécute, SignalR envoie le nom de la méthode et la valeur du paramètre au client, et si le client a une méthode nommée updateStockPrice, l’application appelle cette méthode et lui transmet la valeur du paramètre.

Clients.All signifie envoyer à tous les clients. SignalR vous offre d’autres options pour spécifier les clients ou groupes de clients à envoyer. Pour plus d’informations, consultez HubConnectionContext.

Inscrire l’itinéraire SignalR

Le serveur doit savoir quelle URL intercepter et diriger vers SignalR. Pour ce faire, ajoutez une classe de démarrage OWIN :

  1. Dans Explorateur de solutions, cliquez avec le bouton droit sur le projet et sélectionnez Ajouter un>nouvel élément.

  2. Dans Ajouter un nouvel élément - SignalR.StockTicker, sélectionnez Installé>Visual C#>Web, puis sélectionnez Classe de démarrage OWIN.

  3. Nommez la classe Startup et sélectionnez OK.

  4. Remplacez le code par défaut dans le fichier Startup.cs par ce code :

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

Vous avez maintenant terminé la configuration du code du serveur. Dans la section suivante, vous allez configurer le client.

Configurer le code client

Dans cette section, vous allez configurer le code qui s’exécute sur le client.

Créer la page HTML et le fichier JavaScript

La page HTML affiche les données et le fichier JavaScript organise les données.

Créer des StockTicker.html

Tout d’abord, vous allez ajouter le client HTML.

  1. Dans Explorateur de solutions, cliquez avec le bouton droit sur le projet et sélectionnez Ajouter une>page HTML.

  2. Nommez le fichier StockTicker et sélectionnez OK.

  3. Remplacez le code par défaut dans le fichier StockTicker.html par ce code :

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>ASP.NET SignalR Stock Ticker</title>
        <style>
            body {
                font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
                font-size: 16px;
            }
            #stockTable table {
                border-collapse: collapse;
            }
                #stockTable table th, #stockTable table td {
                    padding: 2px 6px;
                }
                #stockTable table td {
                    text-align: right;
                }
            #stockTable .loading td {
                text-align: left;
            }
        </style>
    </head>
    <body>
        <h1>ASP.NET SignalR Stock Ticker Sample</h1>
    
        <h2>Live Stock Table</h2>
        <div id="stockTable">
            <table border="1">
                <thead>
                    <tr><th>Symbol</th><th>Price</th><th>Open</th><th>Change</th><th>%</th></tr>
                </thead>
                <tbody>
                    <tr class="loading"><td colspan="5">loading...</td></tr>
                </tbody>
            </table>
        </div>
    
        <!--Script references. -->
        <!--Reference the jQuery library. -->
        <script src="/Scripts/jquery-1.10.2.min.js" ></script>
        <!--Reference the SignalR library. -->
        <script src="/Scripts/jquery.signalR-2.1.0.js"></script>
        <!--Reference the autogenerated SignalR hub script. -->
        <script src="/signalr/hubs"></script>
        <!--Reference the StockTicker script. -->
        <script src="StockTicker.js"></script>
    </body>
    </html>
    

    Le code HTML crée une table avec cinq colonnes, une ligne d’en-tête et une ligne de données avec une seule cellule qui s’étend sur les cinq colonnes. La ligne de données indique « chargement... » momentanément quand l’application démarre. Le code JavaScript supprime cette ligne et ajoute à sa place des lignes avec des données de stock récupérées à partir du serveur.

    Les balises de script spécifient :

    • Fichier de script jQuery.

    • Fichier de script principal SignalR.

    • Fichier de script des proxys SignalR.

    • Fichier de script StockTicker que vous créerez ultérieurement.

    L’application génère dynamiquement le fichier de script des proxys SignalR. Il spécifie l’URL « /signalr/hubs » et définit des méthodes proxy pour les méthodes de la classe Hub, dans ce cas, pour StockTickerHub.GetAllStocks. Si vous préférez, vous pouvez générer ce fichier JavaScript manuellement à l’aide des utilitaires SignalR. N’oubliez pas de désactiver la création de fichiers dynamiques dans l’appel de MapHubs méthode.

  4. Dans Explorateur de solutions, développez Scripts.

    Les bibliothèques de scripts pour jQuery et SignalR sont visibles dans le projet.

    Important

    Le gestionnaire de package installera une version ultérieure des scripts SignalR.

  5. Mettez à jour les références de script dans le bloc de code pour qu’elles correspondent aux versions des fichiers de script dans le projet.

  6. Dans Explorateur de solutions, cliquez avec le bouton droit sur StockTicker.html, puis sélectionnez Définir comme page de démarrage.

Créer des StockTicker.js

Créez maintenant le fichier JavaScript.

  1. Dans Explorateur de solutions, cliquez avec le bouton droit sur le projet et sélectionnez Ajouter un>fichier JavaScript.

  2. Nommez le fichier StockTicker et sélectionnez OK.

  3. Ajoutez ce code au fichier StockTicker.js :

    // A simple templating method for replacing placeholders enclosed in curly braces.
    if (!String.prototype.supplant) {
        String.prototype.supplant = function (o) {
            return this.replace(/{([^{}]*)}/g,
                function (a, b) {
                    var r = o[b];
                    return typeof r === 'string' || typeof r === 'number' ? r : a;
                }
            );
        };
    }
    
    $(function () {
    
        var ticker = $.connection.stockTickerMini, // the generated client-side hub proxy
            up = '▲',
            down = '▼',
            $stockTable = $('#stockTable'),
            $stockTableBody = $stockTable.find('tbody'),
            rowTemplate = '<tr data-symbol="{Symbol}"><td>{Symbol}</td><td>{Price}</td><td>{DayOpen}</td><td>{Direction} {Change}</td><td>{PercentChange}</td></tr>';
    
        function formatStock(stock) {
            return $.extend(stock, {
                Price: stock.Price.toFixed(2),
                PercentChange: (stock.PercentChange * 100).toFixed(2) + '%',
                Direction: stock.Change === 0 ? '' : stock.Change >= 0 ? up : down
            });
        }
    
        function init() {
            ticker.server.getAllStocks().done(function (stocks) {
                $stockTableBody.empty();
                $.each(stocks, function () {
                    var stock = formatStock(this);
                    $stockTableBody.append(rowTemplate.supplant(stock));
                });
            });
        }
    
        // Add a client-side hub method that the server will call
        ticker.client.updateStockPrice = function (stock) {
            var displayStock = formatStock(stock),
                $row = $(rowTemplate.supplant(displayStock));
    
            $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
                .replaceWith($row);
            }
    
        // Start the connection
        $.connection.hub.start().done(init);
    
    });
    

Examiner le code client

Si vous examinez le code client, cela vous aidera à découvrir comment le code client interagit avec le code du serveur pour que l’application fonctionne.

Démarrage de la connexion

$.connection fait référence aux proxys SignalR. Le code obtient une référence au proxy pour la StockTickerHub classe et le place dans la ticker variable. Le nom du proxy est le nom qui a été défini par l’attribut HubName :

var ticker = $.connection.stockTickerMini
[HubName("stockTickerMini")]
public class StockTickerHub : Hub

Une fois que vous avez défini toutes les variables et fonctions, la dernière ligne de code du fichier initialise la connexion SignalR en appelant la fonction SignalR start . La start fonction s’exécute de manière asynchrone et retourne un objet jQuery Deferred. Vous pouvez appeler la fonction done pour spécifier la fonction à appeler lorsque l’application termine l’action asynchrone.

$.connection.hub.start().done(init);

Obtenir toutes les actions

La init fonction appelle la getAllStocks fonction sur le serveur et utilise les informations que le serveur retourne pour mettre à jour la table stock. Notez que, par défaut, vous devez utiliser camelCasing sur le client même si le nom de la méthode est pascal-cased sur le serveur. La règle camelCasing s’applique uniquement aux méthodes, pas aux objets. Par exemple, vous faites référence à stock.Symbol et stock.Price, pas stock.symbol ou stock.price.

function init() {
    ticker.server.getAllStocks().done(function (stocks) {
        $stockTableBody.empty();
        $.each(stocks, function () {
            var stock = formatStock(this);
            $stockTableBody.append(rowTemplate.supplant(stock));
        });
    });
}
public IEnumerable<Stock> GetAllStocks()
{
    return _stockTicker.GetAllStocks();
}

Dans la init méthode, l’application crée du code HTML pour une ligne de table pour chaque objet stock reçu du serveur en appelant formatStock pour mettre en forme les propriétés de l’objet stock , puis en appelant supplant pour remplacer les espaces réservés dans la rowTemplate variable par les valeurs de propriété d’objet stock . Le code HTML résultant est ensuite ajouté à la table stock.

Notes

Vous appelez init en le transmettant en tant que callback fonction qui s’exécute une fois la fonction asynchrone start terminée. Si vous appelez init en tant qu’instruction JavaScript distincte après l’appel startde , la fonction échouerait, car elle s’exécuterait immédiatement sans attendre que la fonction de démarrage termine l’établissement de la connexion. Dans ce cas, la init fonction essaie d’appeler la getAllStocks fonction avant que l’application établisse une connexion au serveur.

Obtenir des cours boursiers mis à jour

Lorsque le serveur change le prix d’une action, il appelle les updateStockPrice clients connectés. L’application ajoute la fonction à la propriété cliente du proxy pour la stockTicker rendre disponible pour les appels à partir du serveur.

ticker.client.updateStockPrice = function (stock) {
    var displayStock = formatStock(stock),
        $row = $(rowTemplate.supplant(displayStock));

    $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
        .replaceWith($row);
    }

La updateStockPrice fonction met en forme un objet stock reçu du serveur en une ligne de table de la même façon que dans la init fonction . Au lieu d’ajouter la ligne à la table, il recherche la ligne actuelle du stock dans la table et remplace cette ligne par la nouvelle.

Tester l’application

Vous pouvez tester l’application pour vous assurer qu’elle fonctionne. Vous verrez que toutes les fenêtres du navigateur affichent la table des actions en direct avec les cours des actions fluctuant.

  1. Dans la barre d’outils, activez Le débogage de script , puis sélectionnez le bouton lecture pour exécuter l’application en mode Débogage.

    Capture d’écran de l’utilisateur activant le mode de débogage et sélectionnant lecture.

    Une fenêtre de navigateur s’ouvre affichant la table live stock. La table stock affiche initialement le « chargement... » line, puis, après un court laps de temps, l’application affiche les données boursières initiales, puis les cours des actions commencent à changer.

  2. Copiez l’URL à partir du navigateur, ouvrez deux autres navigateurs et collez les URL dans les barres d’adresse.

    L’affichage initial du stock est identique à celui du premier navigateur et les modifications se produisent simultanément.

  3. Fermez tous les navigateurs, ouvrez un nouveau navigateur et accédez à la même URL.

    L’objet singleton StockTicker a continué à s’exécuter sur le serveur. Le tableau live stock indique que les stocks ont continué à changer. Vous ne voyez pas la table initiale avec des chiffres sans modification.

  4. Fermez le navigateur.

Activation de la journalisation

SignalR dispose d’une fonction de journalisation intégrée que vous pouvez activer sur le client pour faciliter la résolution des problèmes. Dans cette section, vous allez activer la journalisation et voir des exemples qui montrent comment les journaux vous indiquent les méthodes de transport suivantes que SignalR utilise :

Pour une connexion donnée, SignalR choisit la meilleure méthode de transport prise en charge par le serveur et le client.

  1. Ouvrez StockTicker.js.

  2. Ajoutez cette ligne de code mise en surbrillance pour activer la journalisation immédiatement avant le code qui initialise la connexion à la fin du fichier :

    // Start the connection
    $.connection.hub.logging = true;
    $.connection.hub.start().done(init);
    
  3. Appuyez sur F5 pour exécuter le projet.

  4. Ouvrez la fenêtre Outils de développement de votre navigateur, puis sélectionnez la console pour afficher les journaux. Vous devrez peut-être actualiser la page pour voir les journaux de SignalR qui négocient la méthode de transport pour une nouvelle connexion.

    • Si vous exécutez Internet Explorer 10 sur Windows 8 (IIS 8), la méthode de transport est WebSockets.

    • Si vous exécutez Internet Explorer 10 sur Windows 7 (IIS 7.5), la méthode de transport est iframe.

    • Si vous exécutez Firefox 19 sur Windows 8 (IIS 8), la méthode de transport est WebSockets.

      Conseil

      Dans Firefox, installez le complément Firebug pour obtenir une fenêtre console.

    • Si vous exécutez Firefox 19 sur Windows 7 (IIS 7.5), la méthode de transport est des événements envoyés par le serveur .

Installer l’exemple StockTicker

Microsoft.AspNet.SignalR.Sample installe l’application StockTicker. Le package NuGet inclut plus de fonctionnalités que la version simplifiée que vous avez créée à partir de zéro. Dans cette section du tutoriel, vous allez installer le package NuGet et passer en revue les nouvelles fonctionnalités et le code qui les implémente.

Important

Si vous installez le package sans effectuer les étapes précédentes de ce didacticiel, vous devez ajouter une classe de démarrage OWIN à votre projet. Ce fichier readme.txt pour le package NuGet explique cette étape.

Installer le package NuGet SignalR.Sample

  1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Gérer les packages NuGet.

  2. Dans le gestionnaire de package NuGet : SignalR.StockTicker, sélectionnez Parcourir.

  3. Dans Source du package, sélectionnez nuget.org.

  4. Entrez SignalR.Sample dans la zone de recherche, puis sélectionnez Microsoft.AspNet.SignalR.Sample>Installer.

  5. Dans Explorateur de solutions, développez le dossier SignalR.Sample.

    L’installation du package SignalR.Sample a créé le dossier et son contenu.

  6. Dans le dossier SignalR.Sample , cliquez avec le bouton droit surStockTicker.html, puis sélectionnez Définir comme page de démarrage.

    Notes

    L’installation du package NuGet SignalR.Sample peut modifier la version de jQuery que vous avez dans votre dossier Scripts . Le nouveau fichier StockTicker.html que le package installe dans le dossier SignalR.Sample sera synchronisé avec la version jQuery que le package installe, mais si vous souhaitez réexécuter votre fichier StockTicker.html d’origine, vous devrez peut-être d’abord mettre à jour la référence jQuery dans la balise de script.

Exécution de l'application

Le tableau que vous avez vu dans la première application comportait des fonctionnalités utiles. L’application de ticker boursier complet affiche de nouvelles fonctionnalités : une fenêtre de défilement horizontale qui affiche les données boursières et les actions qui changent de couleur à mesure qu’elles augmentent et diminuent.

  1. Appuyez sur F5 pour exécuter l'application.

    Lorsque vous exécutez l’application pour la première fois, le « marché » est « fermé » et vous voyez une table statique et une fenêtre de graduation qui ne défile pas.

  2. Sélectionnez Open Market.

    Capture d’écran du ticker en direct.

    • La zone Live Stock Ticker commence à défiler horizontalement, et le serveur commence à diffuser régulièrement les changements de cours des actions sur une base aléatoire.

    • Chaque fois qu’un cours d’une action change, l’application met à jour à la fois le live stock table et le live stock ticker.

    • Lorsque le changement de prix d’une action est positif, l’application affiche l’action avec un arrière-plan vert.

    • Lorsque la modification est négative, l’application affiche le stock avec un arrière-plan rouge.

  3. Sélectionnez Fermer le marché.

    • Les mises à jour de table s’arrêtent.

    • Le ticker cesse de faire défiler.

  4. Sélectionnez Réinitialiser.

    • Toutes les données boursières sont réinitialisées.

    • L’application restaure l’état initial avant le début des modifications de prix.

  5. Copiez l’URL à partir du navigateur, ouvrez deux autres navigateurs et collez les URL dans les barres d’adresses.

  6. Vous voyez les mêmes données mises à jour dynamiquement en même temps dans chaque navigateur.

  7. Lorsque vous sélectionnez l’un des contrôles, tous les navigateurs répondent de la même façon en même temps.

Affichage live stock ticker

L’affichage Live Stock Ticker est une liste non triée dans un <div> élément mis en forme en une seule ligne par styles CSS. L’application initialise et met à jour le ticker de la même façon que la table : en remplaçant les espaces réservés dans une chaîne de <li> modèle et en ajoutant dynamiquement les <li> éléments à l’élément <ul> . L’application inclut le défilement à l’aide de la fonction jQuery animate pour faire varier la marge gauche de la liste non triée dans le <div>.

SignalR.Sample StockTicker.html

Code HTML du ticker boursier :

<h2>Live Stock Ticker</h2>
<div id="stockTicker">
    <div class="inner">
        <ul>
            <li class="loading">loading...</li>
        </ul>
    </div>
</div>

SignalR.Sample StockTicker.css

Code CSS du ticker boursier :

#stockTicker {
    overflow: hidden;
    width: 450px;
    height: 24px;
    border: 1px solid #999;
    }

    #stockTicker .inner {
        width: 9999px;
    }

    #stockTicker ul {
        display: inline-block;
        list-style-type: none;
        margin: 0;
        padding: 0;
    }

    #stockTicker li {
        display: inline-block;
        margin-right: 8px;   
    }

    /*<li data-symbol="{Symbol}"><span class="symbol">{Symbol}</span><span class="price">{Price}</span><span class="change">{PercentChange}</span></li>*/
    #stockTicker .symbol {
        font-weight: bold;
    }

    #stockTicker .change {
        font-style: italic;
    }

SignalR.Sample SignalR.StockTicker.js

Code jQuery qui le fait défiler :

function scrollTicker() {
    var w = $stockTickerUl.width();
    $stockTickerUl.css({ marginLeft: w });
    $stockTickerUl.animate({ marginLeft: -w }, 15000, 'linear', scrollTicker);
}

Méthodes supplémentaires sur le serveur que le client peut appeler

Pour ajouter de la flexibilité à l’application, il existe des méthodes supplémentaires que l’application peut appeler.

SignalR.Sample StockTickerHub.cs

La StockTickerHub classe définit quatre méthodes supplémentaires que le client peut appeler :

public string GetMarketState()
{
    return _stockTicker.MarketState.ToString();
}

public void OpenMarket()
{
    _stockTicker.OpenMarket();
}

public void CloseMarket()
{
    _stockTicker.CloseMarket();
}

public void Reset()
{
    _stockTicker.Reset();
}

L’application appelle OpenMarket, CloseMarketet Reset en réponse aux boutons en haut de la page. Ils illustrent le modèle d’un client déclenchant un changement d’état immédiatement propagé à tous les clients. Chacune de ces méthodes appelle une méthode dans la StockTicker classe qui provoque le changement d’état du marché, puis diffuse le nouvel état.

SignalR.Sample StockTicker.cs

Dans la StockTicker classe , l’application conserve l’état du marché avec une MarketState propriété qui retourne une MarketState valeur d’énumération :

public MarketState MarketState
{
    get { return _marketState; }
    private set { _marketState = value; }
}

public enum MarketState
{
    Closed,
    Open
}

Chacune des méthodes qui changent l’état du marché le fait à l’intérieur d’un bloc de verrouillage, car la StockTicker classe doit être thread-safe :

public void OpenMarket()
{
    lock (_marketStateLock)
    {
        if (MarketState != MarketState.Open)
        {
            _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
            MarketState = MarketState.Open;
            BroadcastMarketStateChange(MarketState.Open);
        }
    }
}

public void CloseMarket()
{
    lock (_marketStateLock)
    {
        if (MarketState == MarketState.Open)
        {
            if (_timer != null)
            {
                _timer.Dispose();
            }
            MarketState = MarketState.Closed;
            BroadcastMarketStateChange(MarketState.Closed);
        }
    }
}

public void Reset()
{
    lock (_marketStateLock)
    {
        if (MarketState != MarketState.Closed)
        {
            throw new InvalidOperationException("Market must be closed before it can be reset.");
        }
        LoadDefaultStocks();
        BroadcastMarketReset();
    }
}

Pour vous assurer que ce code est thread-safe, le _marketState champ qui sauvegarde la MarketState propriété désigné volatile:

private volatile MarketState _marketState;

Les BroadcastMarketStateChange méthodes et BroadcastMarketReset sont similaires à la méthode BroadcastStockPrice que vous avez déjà vue, sauf qu’elles appellent différentes méthodes définies au niveau du client :

private void BroadcastMarketStateChange(MarketState marketState)
{
    switch (marketState)
    {
        case MarketState.Open:
            Clients.All.marketOpened();
            break;
        case MarketState.Closed:
            Clients.All.marketClosed();
            break;
        default:
            break;
    }
}

private void BroadcastMarketReset()
{
    Clients.All.marketReset();
}

Fonctions supplémentaires sur le client que le serveur peut appeler

La updateStockPrice fonction gère maintenant à la fois le tableau et l’affichage du ticker, et elle utilise jQuery.Color pour flasher les couleurs rouge et vert.

Les nouvelles fonctions dansSignalR.StockTicker.js activer et désactiver les boutons en fonction de l’état du marché. Ils arrêtent ou démarrent également le défilement horizontal Live Stock Ticker . Étant donné que de nombreuses fonctions sont ajoutées à ticker.client, l’application utilise la fonction extension jQuery pour les ajouter.

$.extend(ticker.client, {
    updateStockPrice: function (stock) {
        var displayStock = formatStock(stock),
            $row = $(rowTemplate.supplant(displayStock)),
            $li = $(liTemplate.supplant(displayStock)),
            bg = stock.LastChange === 0
                ? '255,216,0' // yellow
                : stock.LastChange > 0
                    ? '154,240,117' // green
                    : '255,148,148'; // red

        $stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
            .replaceWith($row);
        $stockTickerUl.find('li[data-symbol=' + stock.Symbol + ']')
            .replaceWith($li);

        $row.flash(bg, 1000);
        $li.flash(bg, 1000);
    },

    marketOpened: function () {
        $("#open").prop("disabled", true);
        $("#close").prop("disabled", false);
        $("#reset").prop("disabled", true);
        scrollTicker();
    },

    marketClosed: function () {
        $("#open").prop("disabled", false);
        $("#close").prop("disabled", true);
        $("#reset").prop("disabled", false);
        stopTicker();
    },

    marketReset: function () {
        return init();
    }
});

Configuration du client supplémentaire après l’établissement de la connexion

Une fois que le client a établi la connexion, il a un travail supplémentaire à effectuer :

  • Déterminez si le marché est ouvert ou fermé pour appeler la fonction appropriéemarketOpened.marketClosed

  • Attachez les appels de méthode serveur aux boutons.

$.connection.hub.start()
    .pipe(init)
    .pipe(function () {
        return ticker.server.getMarketState();
    })
    .done(function (state) {
        if (state === 'Open') {
            ticker.client.marketOpened();
        } else {
            ticker.client.marketClosed();
        }

        // Wire up the buttons
        $("#open").click(function () {
            ticker.server.openMarket();
        });

        $("#close").click(function () {
            ticker.server.closeMarket();
        });

        $("#reset").click(function () {
            ticker.server.reset();
        });
    });

Les méthodes de serveur ne sont pas câblées aux boutons tant que l’application n’a pas établi la connexion. Le code ne peut donc pas appeler les méthodes serveur avant qu’elles ne soient disponibles.

Ressources supplémentaires

Dans ce tutoriel, vous avez appris à programmer une application SignalR qui diffuse des messages du serveur vers tous les clients connectés. Vous pouvez désormais diffuser des messages régulièrement et en réponse aux notifications de n’importe quel client. Vous pouvez utiliser le concept de instance singleton multithread pour maintenir l’état du serveur dans les scénarios de jeu en ligne multi-joueurs. Pour obtenir un exemple, consultez le jeu ShootR basé sur SignalR.

Pour obtenir des tutoriels qui montrent les scénarios de communication d’égal à égal, consultez Prise en main avec SignalR et Mise à jour en temps réel avec SignalR.

Pour plus d’informations sur SignalR, consultez les ressources suivantes :

Étapes suivantes

Dans ce tutoriel, vous allez :

  • Création du projet
  • Configurer le code du serveur
  • Examen du code du serveur
  • Configurer le code client
  • Examen du code client
  • Test de l’application
  • Journalisation activée

Passez à l’article suivant pour découvrir comment créer une application web en temps réel qui utilise ASP.NET SignalR 2.