2016年7月18日月曜日

SignalRを少し体験する

ふとしたきっかけでSignalRを実際に使ってみた。
中々に面白く、かつ、思ったよりもさらに簡単なものだとわかった。

Webアプリで、複数ユーザで同期した動作をさせたい場合に、SignalR はとても良い選択肢と感じた。(まあ、WebサーバーはIIS限定、製造は.Net、ってことになってしまうと思いますが。。)

最低限の機能が体感出来るだけの手順を記載してみました。

大まか以下のチュートリアルを元に作成(英語に拒否反応が無い人は、以下を見た方が良い):

Visual Studio のダウンロードは以下より:

(個人で使うのであれば、Community Edition を使えば良い気がする)

●最低限の準備

 
1. Visual Studio を起動して、「File > New > Project...」を選択

Web Applicationを選択し、名前を適当に入力して、「OK」ボタンをクリック
(※プロジェクト名は何でも問題ない。)

続けて表示されるテンプレートの選択で、MVCを選択する
(MVCが個人的に簡単なのでMVCというだけ)


右側の「Choose Authentication」ボタンをクリックし、開いたウィンドウで「No Authentication」を選択し、「OK」ボタンをクリック

元のウィンドウに戻るので、再度「OK」ボタンをクリック

これで、ソリューションとプロジェクトが作成される。

作った直後の状態


2. 作ったままの状態だと、SignalRのコンポーネントが参照されていない状態なので、コンソールで以下コマンドを実行。
install-package Microsoft.AspNet.SignalR

青い線で示した部分に、コマンドを入力

入力したら、Enterキーを押す

SignalR に必要なパッケージやら何やらが、一気にプロジェクトに追加される。
(追加し終わるまで、少し時間がかかります)
最終的に、Successfully Installed とか表示されて、入力待ちに戻ったら、正常に追加されているはず。

Reference、Script等のフォルダ配下に、SignalR向けのファイルが追加されているはず。


3. SignalRの初期化のために、”Startup”クラスを手作業で追加

プロジェクトを右クリックし、「Add > Class」を選択

クラス名として「Startup」を入力し、「Add」ボタンをクリック

クラスの内容を、以下の通り編集する:

using Owin;

namespace SimpleSignalRChat
{
    public class Startup
    {
      public void Configuration(IAppBuilder app)
      {
          app.MapSignalR();
      }
    }
}


上記のクラスが存在すると、アプリの起動時に、勝手にConfigurationメソッドが呼び出され、SignalRの初期化が実行される。
(この、何の変哲もないクラスが初期化時に勝手に動作するという魔術めいた動作は、OWINという仕組みによる)

念のため、この時点でキーボードのF5を押下して、エラー無く起動するかご確認することをお勧めする。
(上記のStartupクラスに相当するクラスが無いと、起動時にエラーで落ちるため)

大雑把なSignalR の仕組み図説


理解した範囲で図示すると、SignalRは、以下のよーな動作をする。
FreshPaint-65-2016.07.17-03.56.12.png

  1. Webブラウザ上から、JavaScriptでサーバ上のHubのメソッドが呼び出される
    コード例)
        $.connection.xxxHub.server.methodName()
  1. サーバ上のHubのpublicメソッドが呼び出され、その中で、クライアントに対して通知が行われる
        XxxHubクラスの、public void methodName() が呼び出される
        メソッド中で、以下のようにクライアント側に通知を行う。
        Clients.All.clientMethod(); // 接続されているすべてのクライアントに
        Clients.Client(Id).clientMethod(); // 特定のクライアントに
  1. Hubで実行されたクライアントに対する通知を受けて、各ブラウザ上でJavaScriptメソッドが同時に実行される
    サーバ側で指定された名前のメソッドが、呼び出される。
    クライアント側で、サーバ側で通知するメソッド名
(このケースだと”clientMethod”)を定義しておくだけで良い。
    $.connection.xxxHub.client.clientMethod = function () {.... };


●最低限の実装


1.Hubクラスの追加

Hubクラスを配置するため、フォルダを追加する。
    a. プロジェクトを右クリックし、「Add > New Folder」をクリック
    b. フォルダ名として「Hubs」を設定

クラスを追加
a.追加したHubフォルダを右クリックし、「Add > Class...」をクリック
b. Web > SignalR の SignalR Hub Class (v2) を選択し、名前に「ChatHub」と入力
     
   c. 「Add」ボタンをクリックして、クラスを追加
    以下の内容に編集:
using Microsoft.AspNet.SignalR;

namespace SimpleSignalRChat.Hubs
{
    public class ChatHub : Hub
    {
      public void Send()
      {
          Clients.All.addMessage();
      }
    }
}
    このクラスのSendメソッドが、クライアント側から呼び出されるメソッド。

2. Controlerに新規メソッド追加
以下メソッドを、HomeControler クラスに追加する
      public ActionResult Chat()
      {
              return View();
      }

3. Viewを追加
    Controlerに追加したメソッドに対応するViewを追加する
    a.プロジェクトのViewsフォルダ配下のHomeフォルダを右クリックして、「Add > View...」を選択
    b. Viewの名前として、Controlerに追加したメソッド名と同じ名前を入力し、「Add」ボタンをクリック
    とりあえず、最小限の見た目を作るため、以下のように編集。
    内容:
@{
    ViewBag.Title = "Chat";
}

<h2>Chat</h2>
<div class="container">
    <input id="sendMessage" type="button" value="Send" />

    <ul id="conversation">

    </ul>
</div>




4. Viewに、Hubの処理を追記
    a. Scriptの追加
    @{
    ViewBag.Title = "Chat";
}

<h2>Chat</h2>
<div class="container">
    <input id="sendMessage" type="button" value="Send" />

    <ul id="conversation"></ul>
</div>

<!-- ここから下が追加部分 -->
@section scripts {

    <!-- SignalR の動作に必要なライブラリ -->
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>

    <!-- SignalR の動作に必要なjsファイル。自動生成される。 -->
    <script src="~/signalr/hubs"></script>

    <script>
      $(function () {
          var chat = $.connection.chatHub;

          // このaddMessage が、サーバ側のChatHubから呼び出される
          chat.client.addMessage = function () {
              $("#conversation").append('<li>' + 'TEST' + '</li>');
          };

          // コネクションを確立し終わった後に、ボタンをクリック時の処理が有効になる
          $.connection.hub.start().done(function () {
              $("#sendMessage").click(
                  function () {
                      // サーバ側のChatHubクラスの、Sendメソッドが呼び出される
                      chat.server.send();
                  }
              );
          })
      });
    </script>
}


5. F5キーを押下して実行する

URLが、http://localhost:xxxxxxx/Home/Chat となっていない場合は、適宜URLを書き換えて再読み込みすること。(xxxxxxx部分は、人によって異なる)

この時点で、複数のブラウザを立ち上げて、ボタンを押すたびに全てのブラウザにほぼ同時にリストの要素が追加されるのが確認できる。
ボタンを連打するなりして、挙動を確認してみていただきたい。

●Hubのメソッドに引数を追加


このままの状態だとチャットアプリとは言えない状態なので、発言者と発言内容を表示するように拡張する。

1.ChatHubクラスのメソッドに、メソッドに引数を追加
 追加後の内容
public void Send(string name, string message)
{ 
    Clients.All.addMessage(name, message);
}

2.Viewクラスに、名前と発言内容を入力するテキストボックスを追加。
    追加後の内容:
<h2>Chat</h2>
<div class="container">
    <input id="name" type="text" value="" />
    <input id="message" type="text" value="" />
    <input id="sendMessage" type="button" value="Send" />
    <ul id="conversation"></ul>
</div>

3.JavaScriptで、Hubクラスのメソッドに値を引き渡すように変更。
    変更後の内容:
          $.connection.hub.start().done(function () {
              $("#sendMessage").click(
                  function () {
                      // サーバ側のChatHubクラスの、Sendメソッドが呼び出される
                      chat.server.send($("#name").val(), $("#message").val());
                  }
              );
          })

4.人の入力を直接表示することになるので、Encoding用のメソッド追加(<script>タグ最後に追加)
    追加する内容:
      function htmlEncode(value) {
              var encodedValue = $('<div />').text(value).html();
              return encodedValue;
      }

5.クライアント側のメソッドに、引数を追加
    追加後の内容:
          // このaddMessage が、サーバ側のChatHubから呼び出される
          chat.client.addMessage = function (name, message) {
              $("#conversation").append('<li>' + htmlEncode(name) + ':' + htmlEncode(message) + '</li>');
          };

この時点まで編集した後、またVisual StudioからF5で起動し、複数ブラウザを立ち上げてもう一度動かすと、入力した値が、全てのブラウザにほぼ同時に反映されるのが確認できるはず。


念のため:変更後のViewのコード全文
@{
    ViewBag.Title = "Chat";
}

<h2>Chat</h2>
<div class="container">
    <input id="name" type="text" value="" />
    <input id="message" type="text" value="" />
    <input id="sendMessage" type="button" value="Send" />

    <ul id="conversation"></ul>
</div>

@section scripts {

    <!-- SignalR + jQuery の動作に必要なライブラリ -->
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>

    <!-- SignalR の動作に必要なjsファイル。自動生成される。 -->
    <script src="~/signalr/hubs"></script>

    <script>
      $(function () {
          var chat = $.connection.chatHub;

          // このaddMessage が、サーバ側のChatHubから呼び出される
          chat.client.addMessage = function (name, message) {
              $("#conversation").append('<li>' + htmlEncode(name) + ':' + htmlEncode(message) + '</li>');
          };

          // コネクションを確立し終わった後に、ボタンをクリックした処理が有効になる
          $.connection.hub.start().done(function () {
              $("#sendMessage").click(
                  function () {
                          // サーバ側のChatHubクラスの、Sendメソッドが呼び出される
                          chat.server.send($("#name").val(), $("#message").val());
                  }
              );
          })
      });

      function htmlEncode(value) {
              var encodedValue = $('<div />').text(value).html();
              return encodedValue;
      }
    </script>
}


以上で、SignalRの最低限の説明は完了である。簡単さと便利さが体感出来たのであれば、幸いです。

少し考えるだけで、チャットアプリ、複数人でプレイするゲーム、遠隔会議ソフトとか、色々と可能性を感じるSignalR。
ただ動かすだけでも面白いでと思うので、興味と機会がある方は、とりあえず使っていただきたいと思う。

以上。

0 件のコメント:

コメントを投稿