次の方法で共有


最新のアプリ

SignalR を使って最新のアプリをビルドする

Rachel Appel

コード サンプルのダウンロード

Rachel Appelブロードバンド インターネットやワイヤレス デバイスへのアクセスが普及したことで、リアルタイム アプリの需要が高まっています。Facebook や Twitter といったよく知られるサイト、マルチプレイヤー ゲーム、ビジネス コラボレーションを実現する業務アプリなどは、リアルタイムのライブ アプリにすることで、最も優れたユーザー エクスペリエンスになります。これからは、株式アプリや財務アプリ、オークション、営業ダッシュボード、電子商取引、教育アプリなど、多くの種類のアプリがリアルタイム エクスペリエンスを備えていくようになるでしょう。ライブ データを必要としない Web サイトやアプリであっても、SignalR によるリアルタイム全二重通信のメリットを利用できる可能性はあります。

SignalR の概要と使用する理由

SignalR は、サーバーとクライアントとの間でのシンプルなリアルタイム双方向通信をサポートするサーバーとクライアントのライブラリのセットです。クライアントからサーバーにアクセスできるだけでなく、Web 開発の場合と同様、サーバーからもクライアントにアクセスできます。どちらからアクセスする場合でも、その内容は単なる HTTP 応答ではなく、プッシュ テクノロジのような、サーバーからクライアントへの実際のメソッド呼び出しです。クライアントは SignalR のサーバー側コンポーネントを経由して、他のクライアントにアクセスすることもできます。このようなことがすべて可能になるのは、SignalR がサーバーとクライアントとの間に固定接続を作成するためです。

多くの開発者が、最新のソフトウェアを作成することを考えています。全二重通信を備えたアプリほど最新のものはないでしょう。SignalR を使用する理由はいくつかあります。Web サイトや Web アプリを簡単に作成できるのも大きな理由の 1 つです。もう 1 つは、開発するソフトウェアでライブ通信が必要な場合です。そのようなときは、SignalR が最適です。WebSocket や AJAX ポーリングなどの多くの技術を駆使すれば、独自のライブ通信を行うことは可能です。ただ、それでは、SignalR チームが既に構築した土台をすべて書き直すようなものです。チームが用意した土台はかなりの拡張性があり、次のような複数の主要機能があります。

  • トランスポート ネゴシエーション: SignalR は、できる限りリアルタイム通信に近い状態になるように、最適なトランスポートを検出します。既定では、WebSocket を使用します。WebSocket はリアルタイム Web アプリを作成するための最速かつ最新の方法です。トランスポートの自動管理は、SignalR のリアルタイム通信を支える考え方で、接続時、クライアントごとにトランスポートのネゴシエーションを行います。
  • SignalR サーバー ホスト: 開発者は、任意の場所 (マイクロソフト以外のプラットフォームを含む) にある簡易自己ホストを選択することも、SignalR を IIS パイプラインにフックすることも可能です。
  • クライアント側のライブラリ: JavaScript を含む、Microsoft .NET Framework と Windows ストアのライブラリ。
  • JavaScript プロキシ: すべてのコードが同じコンピューターの同じプロセスで実行されているかのように開発しても、リモートの場所にあるメソッドを JavaScript から呼び出せるようにします。
  • セキュリティ: SignalR は ASP.NET セキュリティの既存のモデルにフックするため、Microsoft Live、OpenAuth、Google、Facebook、Twitter など、よく使われているサードパーティ製セキュリティ プロバイダーを数多くサポートします。

Web 開発者はこれまで HTTP の要求/応答モデルに従ってコードを作成してきました。本質的には、この作成方法で問題ありませんが、サーバーとクライアントとの間に固定接続が確立されるという SignalR の大きなメリットを活かしていません。HTTP では、要求を行い、応答を受け取って完了です。リアルタイムのシナリオでは、サーバーとクライアントとの間のパイプラインは開いたままになります。その結果、ライブ接続を感じられるリッチで優れたユーザー エクスペリエンスを生み出すことができます。

SignalR では、下位レベルのトランスポート層の上に、ハブと固定接続という 2 つの抽象層を重ねます。今回は、説明を簡潔にするために、ハブだけを取り上げます。ハブは上位レベルの API で、SignalR の極めてシンプルな部分です。固定接続を直接扱うコーディングは時間と労力がかかるため、SignalR ではハブの基盤として固定接続が使用されます。特に理由がない限り、通常はほとんどのアクティビティにハブを使用します。

Windows アプリで SignalR を使ってみる

他の多くの .NET ライブラリと同様、SignalR は NuGet パッケージとして配布されます。NuGet パッケージ マネージャーまたは Package Manager Console を使って、SignalR をインストールできます。どちらも、Visual Studio 2012 および Visual Studio 2013 の機能です。マイクロソフトから入手できる SignalR のパッケージには次のものがあります。

  • Microsoft ASP.NET SignalR: コア コンポーネント、Web コンポーネント、JavaScript クライアントをインストールする基本パッケージ
  • Microsoft ASP.NET SignalR コア コンポーネント: ホスティング ライブラリとコア ライブラリ
  • Microsoft ASP.NET SignalR System.Web: ASP.NET 向け SignalR
  • Microsoft ASP.NET SignalR JavaScript クライアント: HTML アプリ向けの JavaScript クライアント ライブラリ
  • Microsoft ASP.NET SignalR .NET クライアント: その他の Windows プラットフォーム アプリ向けのクライアント ライブラリ

ASP.NET プロジェクト (Web フォームや MVC) に Microsoft ASP.NET SignalR パッケージをインストールすると、SignalR は、上記のパッケージの中で .NET クライアント以外の各パッケージの依存関係をインストールします。.NET クライアントは、Windows 8 や Windows Phone の XAML アプリ、Windows Presentation Foundation (WPF) アプリ、Windows フォーム アプリ、コンソール アプリに対応します。NuGet パッケージ マネージャーには多くの SignalR パッケージが含まれ、マイクロソフト製のパッケージもサードパーティ製のパッケージもあります。このようなパッケージは、自己ホスト、スケーリング、依存関係の挿入、MongoDB サポートなど、ほぼすべてを網羅しています。

インストール後に、web.config の設定を調整する必要はありません。ただし、SignalR をパイプラインに挿入することを ASP.NET に指示するために、以下のような簡単なスタートアップ コードを追加する必要があります。

[assembly: OwinStartup(typeof(VoteR.Startup))]
public partial class Startup
{
  public void Configuration(IAppBuilder app)
  {
    app.MapSignalR();
  }
}

この Startup クラスを App_Start フォルダーにある .cs ファイルに追加します。一部の ASP.NET プロジェクト テンプレートには、ASP.NET フォーム認証用の Startup クラスが既に含まれています。その場合は、このクラスに Configuration メソッドを追加するだけです。これを終えてから、リアルタイム コードの作成に着手します。

この Startup クラスは Open Web Interface for .NET (OWIN) スタートアップです。つまり、新しい OWIN 仕様に準拠しています。OWIN は、Worldwide Web Consortium (W3C) が管理しているのと同様の標準です。マイクロソフトは、Katana というプロジェクトで OWIN を実装しています。OWIN は SignalR の背後で IIS と連動するエンジンで、双方向通信をサポートする自己ホストのようなものです。ハブを使用する SignalR 開発者としては、OWIN や Katana についてこれ以上のことを知る必要はありません。SignalR がすべてを抽象化するため、開発者は SignalR を使ってビジネスの問題を解決することに専念できます。

サーバー側の SignalR ハブ

ハブが SignalR の通信の中核部分になります。ハブは、着信する要求を受け取り、ハブ自体から、または別のクライアントの代理として、クライアントにメッセージをプッシュします。SignalR のハブは、車輪のハブとスポークのようなものです。

ハブは、メッセージングのゲートキーパーにすぎません。ハブはアクションの中心に位置しますが、Microsoft.AspNet.SignalR.Hub クラスから継承する単なるクラスです。Hub クラスは、Microsoft.AspNet.SignalR.Hubs 名前空間の IHub インターフェイスを実装します。この IHub インターフェイスは、OnConnected、OnDisconnected、OnReconnected という 3 つのイベントと、Clients、Context、Groups という 3 つのプロパティを定義します。各イベントやプロパティは、ハブへのリアルタイム接続に関連する共通のタスクや情報を表します。

クライアントは、ハブのパブリック メソッドを呼び出します。つまり、コードは Web サービス呼び出しのようにみえます。ただし、SignalR ハブは、ハブに登録されているクライアントへのアクセスを開始します。通常はこれまでの要求/応答モデルを使用するため、この種の動作を意識してプログラミングすることはありません。

クライアントへのアクセスが可能になるのは、接続しているすべてのクライアントのコレクションを表す Clients プロパティがあるためです。この Clients プロパティを使用して、プラットフォームに関係なく、1 つまたは複数のクライアントにアクセスできます。たとえば、iOS クライアントはハブ経由で Windows クライアントにメッセージを送信できます。ハブは、iOS クライアントの代理として Windows クライアントと通信し、またその逆の立場で通信します。

ハブの動作を確認するために、一連の項目を表示してユーザーにお気に入りの項目を投票してもらう、VoteR というサンプル アプリを見ていきます。このアプリの中核になるのが VoteHub クラスです。このクラスは、ユーザーによる投票数を項目ごとに集計するハブです。集計後は、最新の投票数をクライアントに通知します。図 1 に、VoteHub クラスの例を示します。

図 1 投票数を集計する VoteHub クラス

public class VoteHub : Hub
{
  private static List<Item> VoteItems = new List<Item>();
  private static VoteRContext db = new VoteRContext();
  public void Vote(int id)
  {  
    var votes = AddVote(id);
    Clients.All.updateVoteResults(id, votes);
  }
  private static Item AddVote(int id) {
    var voteItem = VoteItems.Find(v => v.Id == id);       
    if (voteItem != null)
    {
      voteItem.Votes++;
      return voteItem;
    }
    else
    {
      var item = db.Items.Find(id);
      item.Votes++;
      VoteItems.Add(item);
      return item;  
    }       
  }
  public override Task OnConnected()
  {       
    Clients.Caller.joinVoting(VoteItems.ToList());
    return base.OnConnected();
  }
}

図 1 の Vote メソッドと AddVote メソッドを調べます。Vote メソッドは、クライアントが呼び出すメソッドです (後ほど説明します)。クライアントが Vote メソッドを呼び出すと、アプリは、実際に投票集計を行うプライベート AddVote メソッドを呼び出します。プライベート AddVote メソッドは、受け取った項目が VoteItems リストに既に存在するかどうかをチェックします。項目がリストにあれば更新します。リストになければ、つまりユーザーがその項目に初めて投票した場合は項目をリストに追加します。static List<Vote> は、データベースを使わずに、このようなシンプルでグローバルな情報を保管する簡単な方法です。

図 1 の Vote メソッドには、AddVote を呼び出した後に興味深いコード行があります。

Clients.All.updateVoteResults(id, votes);

SignalR では Clients プロパティを使用して、クライアントにアクセスし、クライアントのコードを呼び出します。Clients プロパティから、アクセスするクライアントを指定します。新しい投票を行う場合などは、すべてのクライアントを指定することもできます。ユーザーが最初に接続する場合などは、1 つのクライアントだけを指定することもあります。名前付け規則を使用すると、SignalR はサーバーからの呼び出しと、実行するクライアント上のコードを一致させることができるようになります。クライアントのコードを呼び出すのに動的プロパティを使用できるのは、ASP.NET 史上初めてです。

VoteHub は投票数を追跡する必要があるため、OnConnected のようなイベントがあることに意味があります。OnConnected イベントは、着信する新たな接続を受け取れるようにします。よく使われるのは、Context オブジェクトの ConnectionId プロパティを利用して接続の ID をトラップする方法です。

図 1 の VoteR アプリの場合、VoteHub List<Item> オブジェクト内の 3 つの項目を繰り返し処理し、Clients.caller プロパティを使って各項目の投票数をクライアントに通知します。この方法により、新しく接続したクライアントでも、投票への参加時に項目ごとの合計投票数をすぐに把握できます。

サーバーとクライアントとの間で通信する方法は、他にも多数あります。Hub クラスの Clients プロパティは、クライアント コードにアクセスする多種多様な方法を公開します (図 2 参照)。

図 2 サーバーからクライアントに通信するさまざまな方法

// Call method on all clients
Clients.All.clientSideMethod(args, args, ...);
// Call method on specific client       
Clients.Client(Context.ConnectionId). clientSideMethod(args, args, ...);
// Call a method on a list of specific connections
Clients.Clients(ConnectionId1, ConnectionId1, ...).clientSideMethod(args, args, ...);
// Call method on calling connection
Clients.Caller.clientSideMethod(args, args, ...);
// Call method on all clients but the caller
Clients.Others. clientSideMethod(args, args, ...);
// Call method on all in the group except a few select connections
Clients.AllExcept(connectionId1, connectionId2).clientSideMethod(args, args, ...);
// Call method on groups of connections
Clients.Group(groupName).clientSideMethod(args, args, ...);
// Call method on connected clients in a specified group
Clients.Groups(GroupIds).clientSideMethod(args, args, ...);
// Call method on other connected clients in a specified group
Clients.OthersInGroup(groupName).clientSideMethod(args, args, ...);

図 2 では、サーバーが呼び出す clientSideMethod をクライアントで定義しています。ここからは、クライアントでこのようなメソッドを定義する方法を調べます。Clients プロパティの動的な性質によって、サーバーからクライアントへの幅広い通信シナリオに対応するコードを作成できるようになります。

クライアント側の SignalR: JavaScript クライアントと .NET クライアント

SignalR を使えば、任意のプラットフォームでリアルタイム アプリをビルドできます。追加設定なしで、WinJS アプリなど、Web や HTML のクライアント アプリのすべてに SignalR JavaScript クライアントを使用できます。プレーンな HTML や JavaScript は、広範にサポートされる言語です。.NET に関しては、マイクロソフトが Windows アプリとデスクトップ アプリの両方に対応する .NET クライアントをリリースしています。コア コンポーネントと同様、プロジェクトの種類に応じて、NuGet から JavaScript クライアントまたは .NET クライアントのどちらかをインストールします。JavaScript クライアントは JavaScript にすぎないので、.dll ファイルを参照するのではなく、github.com/SignalR/SignalR (英語) からスクリプトをダウンロードして、次のように <script> タグをページに追加します。

<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.signalR-2.0.3.js"></script>
<script src="~/signalr/hubs"></script>

script 参照の順序は重要です。SignalR は jQuery に依存するため、最初に jQuery を読み込む必要があります。次に読み込むのは SignalR クライアントです。最後に SignalR プロキシを読み込みます。SignalR は実行時に動的にプロキシを生成し、/signalr/hubs に配置します。このプロキシによって、クライアントとサーバーの両方でコードを作成できるようになり、さらに、全部が同じ場所にあるかのように動作するようになります。

VoteR アプリのクライアント スクリプトでは、通常のメソッドとイベントの関連付けだけでなく、サーバーからの呼び出しを受けるメソッドを定義します。図 3 では、先頭に近いコード行で voteHub という変数を設定しています。これにより、VoteHub クラスのインスタンスに直接結び付けられます。SignalR は、接続するクライアントごとにハブのインスタンスを 1 つ作成します。クライアントはプロミスを返す $.connection.hub.start を呼び出して、接続を開始します。つまり、内部のコードは接続が完了するまで実行されません。今回の場合、投票ボタンのクリック イベント内部にあるサーバーの Vote メソッドの呼び出しです。このメソッドにはユーザーが投票している項目の ID が渡されます。その後、サーバーは図 1 で説明した作業を実行します。

図 3 JavaScript クライアント コード

$(function () {
  var voteHub = $.connection.voteHub;
  $.connection.hub.start().done(function () {
    $("button").click(function () {
      voteHub.server.vote(this.id);
        });
  });
  voteHub.client.updateVoteResults = function (id, vote) {
    // Update UI to show each item and how many votes it has
    }
  voteHub.client.joinVoting = function (votes) {
    // Cycle through votes to display current information
    // about each item to newcomer
  }   
});

図 3 を見るとタイプミスがあるように思えます。サーバーとクライアントの VoteHub クラスと Vote メソッドの名前が一致していないように見えます。これはタイプミスではなく、SignalR の表記法です。JavaScript クライアントでは、hub.server.methodName への呼び出しは既定で camelCase 形式に変換されます。大文字小文字を正確に指定して HubName 属性を Hub クラスにアタッチすることで、この動作を簡単に変更できます。HubName 属性は HubName("VoteHub") のようになります。

図 3 のコードの中で最も興味深いのは、voteHub.client.updateVoteResults ブロックと voteHub.client.joinVoting ブロックの 2 つです。2 つのシグニチャが示すように、どちらのメソッドもサーバーの VoteHub の Client プロパティのメンバーです。図 1 をもう一度見直すと、図 3 のクライアント側の voteHub.client.updateVoteResults メソッドは図 1 の Clients.All.updateVoteResults(id, votes) の呼び出しに対応しています。図 4 はサーバーとクライアントのコード間の関係を示しています。

ハブとクライアントのメソッド呼び出しの関係
図 4 ハブとクライアントのメソッド呼び出しの関係

ここからは、.NET クライアントについて見ていきます。図 5 は、C# を使って Windows ストア XAML アプリから接続を行うコードを示しています。コードが同じなので、これを Windows Phone アプリにするのも簡単です。まず、SignalR パイプラインへの接続を作成します。次に、プロキシを作成します。

図 5 にあり、通常の JavaScript クライアントでは見かけない箇所は、HubConnection コンストラクターに渡される SignalR パイプラインへの HTTP パスです。JavaScript クライアントとは違って、それほど自動的ではありません。プロキシを作成するには、HubConnection のインスタンスを作成して CreateHubProxy を呼び出します。

図 5 VoteR で投票する Windows ストア C# クライアント コード

async private Task startConnection()
{
  var hubConnection = new HubConnection("http://localhost:25024/signalr");
  IHubProxy hubProxy = hubConnection.CreateHubProxy("VoteHub");
  var context = SynchronizationContext.Current;
  hubProxy.On<string, string>("updateVoteResults", (id, votes) =>
    context.Post(delegate {
  // Update UI
); }, null));                        
  await hubConnection.Start();
  await hubProxy.Invoke("vote", "rachel", 
    Convert.ToDecimal(itemId.Text));
}

図 5 には、同期コンテキストを得るための変数代入があるのが分かります。このコンテキスト オブジェクトで、サーバーが呼び出すクライアント コードをラップします。次にコンテキスト オブジェクトの Post メソッドを呼び出して、デリゲートを渡します。C# のデリゲートは、JavaScript のインライン匿名関数と同じです。

ハブの接続を開始したら、項目に投票するプロキシの Invoke メソッドを呼び出します。await キーワードを使うと、これらの動作が非同期になります。VoteR デモ アプリの完全なソースは、github.com/rachelappel/VoteR (英語) にあります。

SignalR の配置: サーバーとクライアント

SignalR は ASP.NET なので、自己ホストでない限り、.NET Framework 4.5 以降を含む環境に配置する必要があります。ASP.NET プロジェクトでは、SignalR はライブラリ セットにすぎません。配置時に、他のライブラリと連携します。

SignalR の残りの作業は簡単だと考えているなら、Microsoft Azure への配置を試してみてください。Azure を使うことで、配置のプロセスが特にストレスなく行えるようになります。また、SignalR サーバー コンポーネントと 1 つ以上の HTML クライアントがいずれも Web サ-バーに配置されます。もちろん、適切なチャネルを経由して Windows ストア アプリや Windows Phone アプリをアプリ ストアに、デスクトップ アプリをデスクトップに公開することは必要です。

ASP.NET プロジェクトのアプリであれば、Visual Studio ビルド メニューから [発行] を選択して、Azure への配置を開始できます。Visual Studio 2013 を使用していれば、表示される指示に従うだけです。資格情報を入力してデータベースと Web サイト名を選択します。

Visual Studio 2012 でも、表示される指示は同じです。配置中に、新しい Azure Web サイトを作成するか、既存のサイトを選択するかを選ぶことができます。既存のサイトの場合、Azure ポータルにログインし、[設定] タブに移動して、WebSocket を見つけて有効にします。新しい Web サイトの場合もこの操作が必要ですが、Visual Studio でサイトを作成し最初に起動する動作が、エラーを引き起こします。繰り返しますが、ログインして WebSocket を有効にするだけにします。この点が重要です。設定を変更した後は、Web サイトを停止してから開始することをお勧めします。

まとめ

SignalR は実にシンプルなリアルタイム プログラミングです。ASP.NET 製品であっても、ASP.NET サーバー コンポーネントを使って Windows ストア アプリ、iOS アプリ、Android アプリを作成できるクロス プラットフォーム製品です。マイクロソフト以外の OS でも自己ホストが可能です。このため、SignalR はシンプルかつ効率的なだけでなく、柔軟性も備えています。SignalR のもう 1 つの大きなメリットは、要件としてリアルタイム機能を備える必要がないことです。今後 SignalR を使って、リアルタイム プログライミングのパラダイムを既に導入している多数の開発者の仲間入りをしてください。


Rachel Appel は 20 年を超える IT 業界での経験を持つマイクロソフトの元社員で、コンサルティング、執筆活動、および指導を行っています。彼女は Visual Studio Live!、DevConnections、MIX など、業界トップ クラスのカンファレンスで講演しています。専門分野は、マイクロソフトの各種開発ツールやオープン Web を重視したテクノロジとビジネスを連携させるソリューションの開発です。彼女のことをもっと知りたい方は、彼女の Web サイト rachelappel.com (英語) をご覧ください。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Frank La Vigne に心より感謝いたします。