LoginSignup
28
32

More than 5 years have passed since last update.

SignalR でChat アプリを作ってみた

Last updated at Posted at 2017-12-18

サーバーから、クライアントへの通知を行いたくて、SignalR を調査してみた。サンプルは、JavaScript と、.NET サーバーのものが多かったので、ASP.NET MVC + .NET Client という構成で簡単なサンプルを作ってみた。通知をしたいなら、ポーリングでもいいし、Notification Hub でもいいはず。でもそれらのメリット・デメリットを理解したいので、まず、SignalR をやってみることとにした。

SignalR.png

SignalR とは

SignalR はリアルタイムの双方向通信を簡単におこなうライブラリで、例えばチャットや、対戦ゲーム、プログレスバーみたいな用途に使えるようです。Microsoft で使っているような言語のライブラリが用意されています。問題はWebの情報が古いので動くかドキドキすることですが、意外とあっさり動いてくれました。手順は、VS2012 とかの手順ですが、同じ感じでVS2017でも動作しました。

SignalR の良いところは、双方向通信と聞くと、「Websocketじゃね?」と思うのですが、WebSocketが使えないケースでもServer Sent Events, Forever Frame, Ajax long polling など、Comet ベースのトランスポートもサポートしてくれています。それをライブラリが吸収してくれているらしいです。

アーキテクチャ

下記のようになっていて、サーバー側からクライアントのコードを、クライアントからサーバーのコードを、Hub/HubProxyを通じて双方向に呼べるようになっています。これが隠蔽されていて簡単にコードで書けるのでいい感じですね。

image1.png

HTML5 ベース、そして、Cometベースのトランスポートに対応しています。

image5.png

ちなみに、現時点で NuGetパッケージを検索すると、Angular をはじめ、いろいろなクライアントがサポートされていました。サンプルは、JQueryのサポートだったので、つらかったw。(Javaもあるみたいです)。

今回は、クライアント・サーバー両方 .NET での例で実施してみます。

サーバーのコード

に従えば、ASP.NET MVC + javascript はできてしまうので、違うパターンで。

サーバーはとても簡単で、ASP.NET MVC のテンプレートを使います。(使わなくてもいいです)クラスを追加してみると、SignalR のテンプレートがすでに存在します。

Hub.png

これをプロジェクトを選択して追加します。

次にちょいとエディットします。

ChatHub.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;

namespace SignalRSamples
{
    public class ChatHub : Hub
    {
        public void Send(string name, string message)
        {
            Clients.All.addNewMessageToPage(name, message);
        }
    }
}

このメソッドがクライアントから呼ばれたら、すべてのクライアントに対して、addNewMessageToPageというメッセージを投げるというプログラムです。そこに引数を持っています。

ちなみに、このテンプレートを使うと、こっそり、Reference と、 Javascript のスクリプトに、SignalRのライブラリが追加されていることがわかります。

reference.png

jquery.png

これだけでは、だめなので、Startup.cs クラスを作成します。これもテンプレートで作れます。

Startup.png

Startup.cs

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(SignalRSamples.Startup))]

namespace SignalRSamples
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
            app.MapSignalR();
        }
    }
}

スタートアップ時に、ここで、Application Builder パイプラインに、SignalR を追加して有効化します。ここでデフォルトで、SignalRのエンドポイントは、/singalr になります。これを変更したい場合はこちら参照のこと。変更したい場合は、このapp.MapSignalR() に引数を持たせることができます。

このあたりのStartupがどういうものか?とかの構造に関してはここに解説があるみたい。また読んでみよう。(未読)

そして、デバッグモードで起動する。ちなみに、最初に何故かArgumentOutOfRangeExceptionが出た。

Error.png

StackOverflow だと、IISをいれるといいよだったけど、VS2017 を Administratorモードで動かしたら動くようになった。

こういう直感的ではないエラーはつらいなー。でもなんとかなった。これでまずはサーバー終わり。

クライアント

クライアントは簡単だ。簡単のため、コンソールアプリで作ってみた。NuGetでMicrosoft.AspNet.SignalR.Client をインストールしています。それだけ。

Program.cs

using Microsoft.AspNet.SignalR.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SignalRClient
{
    class Program
    {
        static void Main(string[] args)
        {
            MainAsync(args).GetAwaiter().GetResult();
        }

        static async Task MainAsync(string[] args)
        {

            Console.Write("What is your name? : ");
            var yourname = Console.ReadLine();
            var hubConnection = new HubConnection("http://localhost:1218/signalr"); // 1. Hub へのコネクション
            IHubProxy chatProxy = hubConnection.CreateHubProxy("ChatHub");   // 2. Proxy の作成
            chatProxy.On("addNewMessageToPage", (string name, string message) => // 3. サーバーからのコールバック設定
            {
                Console.WriteLine($"{name} : {message}");
                Console.Write("Message :");
            });
            await hubConnection.Start();  // 4. コネクションの開始

            Console.Write("Message :");
            while (true)
            {
                var yourmessage = Console.ReadLine();
                await chatProxy.Invoke("Send", yourname, yourmessage);  // 4. サーバーのメソッド呼び出し

            }

        }
    }
}

見た目のとおりなのですが、先ほど作成したサーバーへのコネクションを設定します。

var hubConnection = new HubConnection("http://localhost:1218/signalr"); // 1. Hub へのコネクション

つぎにProxyの作成。Hubの名前はサーバー側で作ったものに合わせます。


IHubProxy chatProxy = hubConnection.CreateHubProxy("ChatHub");   // 2. Proxy の作成

Proxyに対して、Onメソッドで、サーバーからのコールバックを設定します。

            chatProxy.On("addNewMessageToPage", (string name, string message) => // 3. サーバーからのコールバック設定
            {
                Console.WriteLine($"{name} : {message}");
                Console.Write("Message :");
            });

先ほどのサーバー側のコードをもう一度見てみましょう。サーバー側からクライアントを読んでいるコードが、Client.All.addNewMessageToPage で、クライアント側のコールバックと引数含めて一致しています。これが呼ばれたら、クライアントのコールバックが呼ばれます。

        public void Send(string name, string message)
        {
            Clients.All.addNewMessageToPage(name, message);
        }

次にサーバー側のメソッド呼び出しです。チャットなので何回もメッセージ送りたいので、ループさせてみました。

            Console.Write("Message :");
            while (true)
            {
                var yourmessage = Console.ReadLine();
                await chatProxy.Invoke("Send", yourname, yourmessage);  // 4. サーバーのメソッド呼び出し

            }

サーバー側の、Send メソッドをプロキシ経由で呼び出しているのがわかると思います。これで双方向通信をよきに計らってくれるってこら簡単だわ!

実行。冒頭のと同じですが、1台のサーバーをあげて、2台のクライアントをあげてチャットしています。

SignalR.png

片方のコンソールでメッセージをうつと、別の方にも即座に反映されます。こらちょっと楽しいなw

ちなみみに、今回のソースコードGitHubに上げています。

次は、Notification Hub とか、これをサーバーレスの環境でどう活用するかとかを楽しんでみたいと思います。ちなみに、私は個人的には、Javascript(JQuery) + ASP.NET MVC のチュートもやってみたのですが、同じぐらい簡単でした。これは、Angular でも簡単なんだろうなぁ。

Resources

28
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
32