shuhelohelo’s blog

Xamarin.Forms多めです.

SignalR Core復習

忘れてしまうのでもう一度基本的な使い方をメモ

参考

docs.microsoft.com

環境

Serviceの追加

Startup.csを開いてConfigureServiceメソッドに以下のようにSignalRを登録します。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSignalR();
        }

次にConfigureメソッドで、ルーティングのエンドポイントとしてSignalRのHubを追加する。

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<MySignalRHub>("/MySignalRHub");//これ
            });
        }

MySignalRHubクラスはメッセージの受信、送信を行うHubの役割を担うクラスで、後で作成する。

ここでは、URLホスト名/{MySignalRHub}に対してクライアントから接続があった場合にMySignalRHubクラスを介してメッセージがやり取りされるように指定している。

Hub(クラスを継承した)クラスの実装(サーバー側の実装)

このHubクラスはクライアントからメッセージを受け取ったら、そのメッセージをすべてのクライアントに対して送信したり、特定のクライアントに送信したりする。

その名のとおりメッセージングのHubの役割を果たすもので、そのためのメソッドをここに定義しておく。

クライアント側からはこのHubクラス内に定義されたメソッドを呼び出してメッセージを送信する。

今回はクライアントから受け取ったメッセージを接続中のすべてのクライアントに対して送信するメソッドSendToAllを定義しておく。

    public class MySignalRHub : Hub
    {
        /// <summary>
        /// クライアントから任意の個数のデータを受け取ることができる。今回は1個。
        /// クライアントはこのメソッドを呼び出す。
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendToAllAsync(string message)
        {
            //Client側のReceiveMessageメソッドを呼ぶ
            //今回は受信したメッセージにタイムスタンプをつけてべてのクライアントに送信するだけ。
            await Clients.All.SendAsync("ReceiveMessage", AppendTimeStamp(message));
        }

        private string AppendTimeStamp(string message)
        {
            return $"{message} : {DateTimeOffset.Now.ToString("yyyy/MM/dd/HH:mm:ss.fff")}";
        }
    }

サーバーサイドはこれだけ。

Hubクラスの中に任意のメソッドを用意するだけ。

サーバー側の動作確認

ここまででサーバー側の動作を確認します。 デバッグ実行し、{ホスト名}/mysignalrhubにブラウザからアクセスします。

ホスト名の部分はポート番号が異なる場合もありますが、私の場合は以下のとおりです。

https://localhost:44304/mysignalrhub

そして、以下のようなメッセージが表示されればOKです。

f:id:shuhelohelo:20200210123702p:plain

クライアント側の実装

先程作成したサーバー側のSignalRハブへ接続してメッセージを送信、受信するクライアント側を用意します。

今回は、クライアントはC#のコンソールアプリで作ります。

コンソールアプリのプロジェクトを作成します。

SignalRClientライブラリのインストール

クライアントからSignalRハブに接続するには以下のライブラリが必要になるので、Nugetなどからインストールします。

Microsoft.AspNetCore.SignalR.Client

接続用オブジェクトを生成

今回のクライアントのサンプルはコンソールアプリで、基本的にはMainメソッド内に書いていっています。

まずは以下のようにサーバー側との通信を担うオブジェクトを用意します。

難しいことはなく、HubConnectionBuilderオブジェクトを使って接続に必要な情報などを設定して、最終的にHubConnectionオブジェクトを取得します。

            //HubConnectionオブジェクトを使ってサーバーのSignalRハブと通信する
            HubConnection connection;

            //SignalRハブとの通信を担うオブジェクトを生成
            connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:44304/mysignalrhub")
                .WithAutomaticReconnect()
                .Build();

以後、このHubConnectionオブジェクトを介してサーバー側のSignalRハブとメッセージの送受信を行います。

さて、サーバー側ではメッセージを受け取ったらクライアント側のメソッドをReceiveMessageという名前で呼び出すように指定しました。

await Clients.All.SendAsync("ReceiveMessage", AppendTimeStamp(message));

しかし、この名前で呼び出されるクライアント側メソッドが何なのかはサーバーも、HubConnectionオブジェクトもこの時点では知ることができません。

なので、サーバー側からこの名前で呼出があったらクライアントのどのメソッドを実行するのかをHubConnectionオブジェクトに登録します。

以下のように、HubConnectionオブジェクトのOnメソッドで指定します。

connection.On<string>("ReceiveMessage", ReceiveMessageClient);

//~~~~~~~~~~~~~~~~~~~~~~~

        private static void ReceiveMessageClient(string messageFromServer)
        {
            Console.WriteLine($"サーバーから{DateTimeOffset.Now.ToString("yyyy/MM/dd/HH:mm:ss.fff")}に受信:\n {messageFromServer}");
        }

このとき、第1引数に渡している文字列には、サーバー側で指定している文字列と同じ文字列を指定します。

これが一致していないとクライアント側のメソッドが呼び出されません。

f:id:shuhelohelo:20200210134315p:plain

接続

SignalRハブとは以下のように接続します。

            try
            {
                //サーバー側のSignalRハブと接続
                await connection.StartAsync();
                Console.WriteLine("接続できました");
            }
            catch (Exception ex)
            {
                Console.WriteLine("接続できませんでした");
            }

メッセージ送信

メッセージの送信は以下のようにします。

                string input = Console.ReadLine();

                try
                {
                    //メッセージ送信
                    await connection.InvokeAsync<string>("SendToAllAsync", input);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("メッセージの送信に失敗しました");
                }

クライアント側全コード

    internal class Program
    {
        private static async Task Main(string[] args)
        {
            //HubConnectionオブジェクトを使ってサーバーのSignalRハブと通信する
            HubConnection connection;

            //SignalRハブとの通信を担うオブジェクトを生成
            connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:44304/mysignalrhub")
                .WithAutomaticReconnect()
                .Build();

            //クライアント側の受信時の処理を行うメソッドを登録する。
            //文字列で指定した名前でサーバー側から呼び出される。
            connection.On<string>("ReceiveMessage", ReceiveMessageClient);

            try
            {
                //サーバー側のSignalRハブと接続
                await connection.StartAsync();
                Console.WriteLine("接続できました");
            }
            catch (Exception ex)
            {
                Console.WriteLine("接続できませんでした");
            }

            while (true)
            {
                Console.Write("メッセージを入力: ");

                string input = Console.ReadLine();

                try
                {
                    //メッセージ送信
                    await connection.InvokeAsync<string>("SendToAllAsync", input);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("メッセージの送信に失敗しました");
                }

                Thread.Sleep(500);
            }
        }

        /// <summary>
        /// メッセージ受信時に実行される
        /// </summary>
        /// <param name="messageFromServer"></param>
        private static void ReceiveMessageClient(string messageFromServer)
        {
            Console.WriteLine($"サーバーから{DateTimeOffset.Now.ToString("yyyy/MM/dd/HH:mm:ss.fff")}に受信:\n {messageFromServer}");
        }
    }

実行

まずサーバー側をデバッグ実行する。

次にクライアントをデバッグ実行する。

クライアント側のコンソールでは、サーバーと接続できた場合に「接続できました」とメッセージが表示されているはずです。

その後はメッセージの入力を求められるので、任意の文字列を入力してEnterキーを押すと、メッセージがサーバー側に送信されます。

サーバー側では受け取ったメッセージにタイムスタンプを追加してクライアントに送信します。

f:id:shuhelohelo:20200210141743p:plain