チュートリアル: SignalR 2 を使用した高頻度のリアルタイムアプリの作成

このチュートリアルでは、ASP.NET SignalR 2 を使用して高頻度のメッセージング機能を提供する web アプリケーションを作成する方法について説明します。 この場合、"高頻度のメッセージング" とは、サーバーが更新を一定の速度で送信することを意味します。 1秒あたり最大10個のメッセージを送信します。

作成したアプリケーションには、ユーザーがドラッグできる図形が表示されます。 サーバーは、接続されているすべてのブラウザー内の図形の位置を更新し、時間指定の更新を使用して、ドラッグした図形の位置と一致します。

このチュートリアルで導入された概念には、リアルタイムゲームやその他のシミュレーションアプリケーションのアプリケーションがあります。

このチュートリアルでは、次の作業を行いました。

  • プロジェクトのセットアップ
  • 基本アプリケーションを作成する
  • アプリの起動時にハブにマップする
  • クライアントを追加する
  • アプリを実行する
  • クライアントループを追加する
  • サーバーループの追加
  • スムーズなアニメーションの追加

警告

このドキュメントは、SignalR の最新バージョンではありません。 ASP.NET Core SignalRをご覧ください。

[前提条件]

プロジェクトのセットアップ

このセクションでは、Visual Studio 2017 でプロジェクトを作成します。

このセクションでは、Visual Studio 2017 を使用して空の ASP.NET Web アプリケーションを作成し、SignalR ライブラリと jQuery ライブラリを追加する方法について説明します。

  1. Visual Studio で、ASP.NET Web アプリケーションを作成します。

    Create web

  2. [新しい ASP.NET Web アプリケーション-MoveShapeDemo ] ウィンドウで、のままにして [ OK]を選択します。

  3. ソリューションエクスプローラーで、プロジェクトを右クリックし、[新しい項目追加> ] を選択します。

  4. [新しい項目の追加-MoveShapeDemo] で、[インストールされている>Visual C#>Web>SignalR ] を選択し、[ SignalR Hub クラス (v2)] を選択します。

  5. クラスに MoveShapeHub という名前を指定し、プロジェクトに追加します。

    この手順では、 MoveShapeHub クラスファイルを作成します。 同時に、SignalR をサポートする一連のスクリプトファイルとアセンブリ参照をプロジェクトに追加します。

  6. [ツール]>[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] の順に選択します。

  7. パッケージマネージャーコンソールで、次のコマンドを実行します。

    Install-Package jQuery.UI.Combined
    

    コマンドを実行すると、jQuery UI ライブラリがインストールされます。 図形をアニメーション化するために使用します。

  8. ソリューションエクスプローラーで、[スクリプト] ノードを展開します。

    Script library references

    JQuery、jQueryUI、SignalR のスクリプトライブラリは、プロジェクトに表示されます。

基本アプリケーションを作成する

このセクションでは、ブラウザーアプリケーションを作成します。 アプリは、各マウス移動イベントの発生時に、図形の場所をサーバーに送信します。 サーバーは、この情報を、接続されている他のすべてのクライアントにリアルタイムでブロードキャストします。 このアプリケーションの詳細については、後のセクションで説明します。

  1. MoveShapeHubファイルを開きます。

  2. MoveShapeHubファイル内のコードを次のコードに置き換えます。

    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class MoveShapeHub : Hub
        {
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);
            }
        }
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
            [JsonProperty("top")]
            public double Top { get; set; }
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
    }
    
  3. ファイルを保存します。

MoveShapeHubクラスは、SignalR hub の実装です。 SignalR でのはじめにチュートリアルと同様、ハブにはクライアントが直接呼び出すメソッドがあります。 この場合、クライアントは図形の新しい X 座標と Y 座標を使用してオブジェクトをサーバーに送信します。 これらの座標は、接続されている他のすべてのクライアントにブロードキャストされます。 SignalR は、このオブジェクトを JSON を使用して自動的にシリアル化します。

アプリケーションがオブジェクトをクライアントに送信 ShapeModel します。 図形の位置を格納するメンバーがあります。 サーバー上のオブジェクトのバージョンには、どのクライアントのデータが格納されているかを追跡するメンバーもあります。 このオブジェクトは、サーバーがクライアントのデータをそれ自体に送信しないようにします。 このメンバーは、 JsonIgnore 属性を使用して、アプリケーションがデータをシリアル化してクライアントに送り返すことを防止します。

アプリの起動時にハブにマップする

次に、アプリケーションの起動時に、ハブへのマッピングを設定します。 SignalR 2 では、OWIN startup クラスを追加するとマッピングが作成されます。

  1. ソリューションエクスプローラーで、プロジェクトを右クリックし、[新しい項目追加> ] を選択します。

  2. [新しい項目の追加-MoveShapeDemo ] で、[インストールされている>Visual C#>Web ] を選択し、[ OWIN Startup Class] を選択します。

  3. クラスに「 Startup 」という名前を指定し、[ OK]を選択します。

  4. スタートアップファイルの既定のコードを次のコードに置き換えます。

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

OWIN startup クラスは、アプリがメソッドを Configuration 実行するときにを呼び出し MapSignalR ます。 このアプリは、アセンブリ属性を使用して OwinStartup OWIN のスタートアッププロセスにクラスを追加します。

クライアントを追加する

クライアントの HTML ページを追加します。

  1. ソリューションエクスプローラーで、プロジェクトを右クリックし、[HTML ページ追加> ] を選択します。

  2. ページの 既定 の名前を指定し、[ OK]を選択します。

  3. ソリューションエクスプローラーで、[ Default.htmlを右クリックし、[スタートページとして設定] を選択します。

  4. Default.htmlファイルの既定のコードを次のコードに置き換えます。

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
     $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                $shape = $("#shape"),
                shapeModel = {
                    left: 0,
                    top: 0
                };
                moveShapeHub.client.updateShape = function (model) {
                    shapeModel = model;
                    $shape.css({ left: model.left, top: model.top });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moveShapeHub.server.updateModel(shapeModel);
                        }
                    });
                });
            });
    </script>
        
        <div id="shape" />
    </body>
    </html>
    
  5. ソリューションエクスプローラーで、[スクリプト] を展開します。

    JQuery と SignalR のスクリプトライブラリは、プロジェクトに表示されます。

    重要

    パッケージマネージャーでは、SignalR スクリプトの新しいバージョンがインストールされます。

  6. コードブロック内のスクリプト参照を、プロジェクト内のスクリプトファイルのバージョンに対応するように更新します。

この HTML および JavaScript コードでは、という shape 赤色 div のが作成されます。 JQuery ライブラリを使用して図形のドラッグ動作を有効にし、 drag イベントを使用して図形の位置をサーバーに送信します。

アプリを実行する

アプリを実行して、作業することができます。 図形をブラウザーウィンドウの周りにドラッグすると、他のブラウザーでも図形は移動します。

  1. ツールバーの [ スクリプトデバッグ ] をオンにし、[再生] ボタンを選択して、アプリケーションをデバッグモードで実行します。

    Screenshot of user turning on debugging mode and selecting play.

    ブラウザーウィンドウが開き、右上隅に赤色の図形が表示されます。

  2. ページの URL をコピーします。

  3. 別のブラウザーを開き、アドレスバーに URL を貼り付けます。

  4. いずれかのブラウザーウィンドウで図形をドラッグします。 他のブラウザーウィンドウの図形は次のようになります。

アプリケーションはこのメソッドを使用して機能しますが、推奨されるプログラミングモデルではありません。 送信されるメッセージの数に上限はありません。 その結果、クライアントとサーバーはメッセージを処理できないため、パフォーマンスが低下します。 また、アプリによって、クライアントに不整合なアニメーションが表示されます。 この不自然なアニメーションは、図形が各メソッドによって瞬時に移動するために発生します。 図形を新しい場所に滑らかに移動させる方が適切です。 次に、これらの問題を解決する方法を学習します。

クライアントループを追加する

マウスの移動イベントごとに図形の場所を送信すると、不必要な量のネットワークトラフィックが発生します。 アプリでは、クライアントからのメッセージを調整する必要があります。

Javascript setInterval 関数を使用して、固定レートでサーバーに新しい位置情報を送信するループを設定します。 このループは、"ゲームループ" の基本的な表現です。これは、ゲームのすべての機能を駆動する、繰り返し呼び出される関数です。

  1. 次のコードを使用して、Default.html内 のクライアント コードを置き換えます。

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

    重要

    スクリプト参照を再度置き換える必要があります。 これらは、プロジェクト内のスクリプトのバージョンと一致する必要があります。

    この新しいコードでは、 関数が追加 updateServerModel されます。 固定の頻度で呼び出されます。 この関数は、 フラグが moved 送信する新しい位置データが指定されるたびに、位置データをサーバーに送信します。

  2. 再生ボタンを選択してアプリケーションを起動する

  3. ページの URL をコピーします。

  4. 別のブラウザーを開き、アドレス バーに URL を貼り付けます。

  5. ブラウザー ウィンドウの 1 つで図形をドラッグします。 他のブラウザー ウィンドウの図形が後に続きます。

アプリはサーバーに送信されるメッセージの数を調整します。そのため、アニメーションは最初はスムーズに表示されません。

サーバー ループを追加する

現在のアプリケーションでは、サーバーからクライアントに送信されたメッセージは、受信した頻度で送信されます。 このネットワーク トラフィックは、クライアントで見たのと同様の問題を示します。

アプリは、必要以上に頻繁にメッセージを送信できます。 その結果、接続がフラッドになる可能性があります。 このセクションでは、送信メッセージの速度を調整するタイマーを追加するためにサーバーを更新する方法について説明します。

  1. MoveShapeHub.cs の内容を次のコードに置き換えます。

    using System;
    using System.Threading;
    using Microsoft.AspNet.SignalR;
    using Newtonsoft.Json;
    
    namespace MoveShapeDemo
    {
        public class Broadcaster
        {
            private readonly static Lazy<Broadcaster> _instance = 
                new Lazy<Broadcaster>(() => new Broadcaster());
            // We're going to broadcast to all clients a maximum of 25 times per second
            private readonly TimeSpan BroadcastInterval = 
                TimeSpan.FromMilliseconds(40); 
            private readonly IHubContext _hubContext;
            private Timer _broadcastLoop;
            private ShapeModel _model;
            private bool _modelUpdated;
            public Broadcaster()
            {
                // Save our hub context so we can easily use it 
                // to send to its connected clients
                _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
                _model = new ShapeModel();
                _modelUpdated = false;
                // Start the broadcast loop
                _broadcastLoop = new Timer(
                    BroadcastShape, 
                    null, 
                    BroadcastInterval, 
                    BroadcastInterval);
            }
            public void BroadcastShape(object state)
            {
                // No need to send anything if our model hasn't changed
                if (_modelUpdated)
                {
                    // This is how we can access the Clients property 
                    // in a static hub method or outside of the hub entirely
                    _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
                    _modelUpdated = false;
                }
            }
            public void UpdateShape(ShapeModel clientModel)
            {
                _model = clientModel;
                _modelUpdated = true;
            }
            public static Broadcaster Instance
            {
                get
                {
                    return _instance.Value;
                }
            }
        }
            
        public class MoveShapeHub : Hub
        {
            // Is set via the constructor on each creation
            private Broadcaster _broadcaster;
            public MoveShapeHub()
                : this(Broadcaster.Instance)
            {
            }
            public MoveShapeHub(Broadcaster broadcaster)
            {
                _broadcaster = broadcaster;
            }
            public void UpdateModel(ShapeModel clientModel)
            {
                clientModel.LastUpdatedBy = Context.ConnectionId;
                // Update the shape model within our broadcaster
                _broadcaster.UpdateShape(clientModel);
            }
        }
        public class ShapeModel
        {
            // We declare Left and Top as lowercase with 
            // JsonProperty to sync the client and server models
            [JsonProperty("left")]
            public double Left { get; set; }
            [JsonProperty("top")]
            public double Top { get; set; }
            // We don't want the client to get the "LastUpdatedBy" property
            [JsonIgnore]
            public string LastUpdatedBy { get; set; }
        }
        
    }
    
  2. 再生ボタンを選択して、アプリケーションを起動します。

  3. ページの URL をコピーします。

  4. 別のブラウザーを開き、アドレス バーに URL を貼り付けます。

  5. ブラウザー ウィンドウの 1 つで図形をドラッグします。

このコードは、クライアントを展開して クラスを追加 Broadcaster します。 新しいクラスは、.NET Framework の クラス Timer を使用して送信メッセージを調整します。

ハブ自体が一過性であるという学習を行うのは良い方法です。 必要に応じて作成されます。 そのため、アプリは シングルトンとして Broadcaster を作成します。 遅延初期化を使用して、必要 Broadcasterになるまで の作成を遅延します。 そうすると、タイマーを開始する前に、アプリによって最初のハブ インスタンスが完全に作成されます。

その後、クライアントの関数の UpdateShape 呼び出しは、ハブの メソッドから移動 UpdateModel されます。 アプリが受信メッセージを受信するたびにすぐに呼び出されなくなりました。 代わりに、アプリは 1 秒あたり 25 回の呼び出しの速度でクライアントにメッセージを送信します。 プロセスは、 クラス内のタイマー _broadcastLoop によって管理 Broadcaster されます。

最後に、 クラスは、 Broadcaster ハブからクライアント メソッドを直接呼び出す代わりに、現在動作しているハブへの参照を取得する必要 _hubContext があります。 を使用して参照を取得します GlobalHost

スムーズ アニメーションを追加する

アプリケーションはほぼ完了しましたが、もう 1 つ改善できます。 アプリは、サーバー メッセージに応答してクライアント上の図形を移動します。 図形の位置をサーバーによって指定された新しい場所に設定する代わりに、JQuery UI ライブラリの 関数を使用 animate します。 図形を現在の位置と新しい位置の間でスムーズに移動できます。

  1. 強調表示されているコードのように、updateShapeDefault.htmlファイル内のクライアントの メソッドを更新します。

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR MoveShape Demo</title>
        <style>
            #shape {
                width: 100px;
                height: 100px;
                background-color: #FF0000;
            }
        </style>
    </head>
    <body>
    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.1.0.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
            $(function () {
                var moveShapeHub = $.connection.moveShapeHub,
                    $shape = $("#shape"),
                    // Send a maximum of 10 messages per second 
                    // (mouse movements trigger a lot of messages)
                    messageFrequency = 10, 
                    // Determine how often to send messages in
                    // time to abide by the messageFrequency
                    updateRate = 1000 / messageFrequency, 
                    shapeModel = {
                        left: 0,
                        top: 0
                    },
                    moved = false;
                moveShapeHub.client.updateShape = function (model) {
                     shapeModel = model;
                     // Gradually move the shape towards the new location (interpolate)
                     // The updateRate is used as the duration because by the time 
                     // we get to the next location we want to be at the "last" location
                     // We also clear the animation queue so that we start a new 
                     // animation and don't lag behind.
                     $shape.animate(shapeModel, { duration: updateRate, queue: false });
                };
                $.connection.hub.start().done(function () {
                    $shape.draggable({
                        drag: function () {
                            shapeModel = $shape.offset();
                            moved = true;
                        }
                    });
                    // Start the client side server update interval
                    setInterval(updateServerModel, updateRate);
                });
                function updateServerModel() {
                    // Only update server if we have a new movement
                    if (moved) {
                        moveShapeHub.server.updateModel(shapeModel);
                        moved = false;
                    }
                }
            });
    </script>
       
        <div id="shape" />
    </body>
    </html>
    
  2. 再生ボタンを選択して、アプリケーションを起動します。

  3. ページの URL をコピーします。

  4. 別のブラウザーを開き、アドレス バーに URL を貼り付けます。

  5. ブラウザー ウィンドウの 1 つで図形をドラッグします。

もう一方のウィンドウでの図形の動きは、あまり見えなさそうに見えます。 アプリは、受信メッセージごとに 1 回設定されるのではなく、時間の渡る移動を補間します。

このコードは、図形を古い場所から新しい場所に移動します。 サーバーは、アニメーション間隔の過程で図形の位置を指定します。 この場合は 100 ミリ秒です。 アプリは、新しいアニメーションが開始される前に、図形で実行されている以前のアニメーションをクリアします。

コードの入手

完了したファイルをダウンロードProject

その他のリソース

ここで学習した通信パラダイムは、SignalR で作成された、GameR ゲームなどのオンライン ゲームや他のシミュレーションを開発する場合 に役立ちます

SignalR の詳細については、次のリソースを参照してください。

次のステップ

このチュートリアルでは、次の作業を行いました。

  • プロジェクトのセットアップ
  • 基本アプリケーションを作成しました
  • アプリの起動時にハブにマップされる
  • クライアントを追加しました
  • アプリを実行しました
  • クライアント ループを追加しました
  • サーバー ループを追加しました
  • スムーズ アニメーションを追加しました

次の記事に進み、ASP.NET SignalR 2 を使用してサーバー ブロードキャスト機能を提供する Web アプリケーションを作成する方法について説明します。