C#でSignalRを使用した通信を行いたい その7(サーバがクライアントにデータを渡す) | プログラミングがわからなすぎる

プログラミングがわからなすぎる

気が向いた時の備忘録。プログラミングは好きなのに物凄く単純なものしか理解できないからメモしていくうちに覚えられたらいいな

サーバがクライアントに呼び掛けて、データを渡してくる処理のサンプルを

作って動かしてみた

 

これをやるとたぶん「C#でSignalRを使用した通信を行いたい その4

で書いたReceivedイベントが発生します

たぶん・・・

 

クライアント側のプロジェクト 

前回までに設置してたけど使ってなかった下のほうのコントロールを使います

チャットで送りたい言葉を入力するテキストボックスと

言葉を全員に送る「発言」ボタンです

 

それぞれの部品名はこのようなかんじ

 

 Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SignalRClientTest
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            ClientForm form = new ClientForm();
            FormSet.Form = form;
            Application.Run(form);
        }
    }
}

前までのサーバ側と同じく、Form画面のインスタンスを

ちょっとよけて取っといてもらうことにした

 

 FormSet.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SignalRClientTest
{
    public static class FormSet
    {
        /// <summary>
        /// フォーム
        /// </summary>
        public static ClientForm Form;
    }
}

画面のインスタンスを持っててくれる人

 

 ClientForm.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SignalRClientTest
{
    public partial class ClientForm : Form
    {
        public ClientForm()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 接続ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ConnectButton_Click(object sender, EventArgs e)
        {
            bool result = SignalRClient.Connect();
            if (result)
                MessageBox.Show("接続成功");
            else
                MessageBox.Show("接続失敗");
        }

        /// <summary>
        /// 切断ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisConnectButton_Click(object sender, EventArgs e)
        {
            SignalRClient.DisConnect();
        }

        /// <summary>
        /// 設定ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SendNameButton_Click(object sender, EventArgs e)
        {
            SignalRClient.SendName(NameTextBox.Text);
        }

        /// <summary>
        /// 参加者の確認ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void GetNameButton_Click(object sender, EventArgs e)
        {
            List<string> names = await SignalRClient.GetNames();

            string nameStr = "";
            foreach (string name in names)
            {
                if (nameStr == "")
                    nameStr = name;
                else
                    nameStr += "、" + name;
            }

            if (names.Count > 0)
                TalkRichTextBox.Text += nameStr + "が参加中です。\r\n";
            else
                TalkRichTextBox.Text += "参加者は居ません。\r\n";
        }

        /// <summary>
        /// 退出ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void ExitButton_Click(object sender, EventArgs e)
        {
            bool result = await SignalRClient.ExitRoom(NameTextBox.Text);
            if (result)
                TalkRichTextBox.Text += "退出しました\r\n";
            else
                TalkRichTextBox.Text += "退出に失敗しました\r\n";
        }

        /// <summary>
        /// 発言ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SendTalkButton_Click(object sender, EventArgs e)
        {
            string msg = TalkTextBox.Text;
            SignalRClient.SendMessage(msg);
        }

        /// <summary>
        /// メッセージ表示
        /// </summary>
        /// <param name="msg"></param>
        public void WriteTalk(string msg)
        {
            TalkRichTextBox.Text += msg + "\r\n";
        }

    }
}

 

「発言」ボタンをクリックしたときの処理SendTalkButton_Clickと

サーバから誰かの発言を受け取った時にリッチテキストボックスに

表示してくれるWriteTalkを追加した

 

SendTalkButton_ClickはSignalRGlientクラスのSendMessage()に

TalkTextBoxの内容を投げてるだけです

 

 SignalRClientTest.cs

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

namespace SignalRClientTest
{
    class SignalRClient
    {
        private static HubConnection hub;
        private static IHubProxy proxy;
        private const string URL = "http://localhost:8088";
        private const string HUB_CLASS = "TestHub";

        /// <summary>
        /// 接続処理
        /// </summary>
        /// <returns>結果 true:成功 false:失敗</returns>
        public static bool Connect()
        {
            try
            {
                hub = new HubConnection(URL);
                proxy = hub.CreateHubProxy(HUB_CLASS);

                proxy.On<string>("CatchTalk", CatchTalk);
                hub.Start().Wait(); // 接続実行
            }
            catch
            {
                // 接続失敗
                return false;
            }
            return true;
        }

        /// <summary>
        /// 切断処理
        /// </summary>
        public static void DisConnect()
        {
            hub?.Stop();
            hub?.Dispose();
        }

        /// <summary>
        /// 名前送信
        /// </summary>
        /// <param name="name"></param>
        public static void SendName(string name)
        {
            proxy.Invoke("AnnounceSendName");
            proxy.Invoke("GetName", name);
        }

        /// <summary>
        /// 参加者の取得処理
        /// </summary>
        /// <returns></returns>
        public static async Task<List<string>> GetNames()
        {
            List<string> namelist = await proxy.Invoke<List<string>>("SendOurNames");
            return namelist;
        }

        /// <summary>
        /// 退出処理
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static async Task<bool> ExitRoom(string name)
        {
            bool ret = await proxy.Invoke<bool>("ExitChatRoom", name);
            return ret;
        }

        /// <summary>
        /// メッセージを送信
        /// </summary>
        /// <param name="msg"></param>
        public static void SendMessage(string msg)
        {
            proxy.Invoke("CatchMessage", msg);
        }

        /// <summary>
        /// メッセージ受け取り
        /// </summary>
        /// <param name="msg"></param>
        public static void CatchTalk(string msg)
        {
            FormSet.Form.Invoke(new Action<string>(FormSet.Form.WriteTalk), msg);
        }

    }
}

ClientFormから呼ばれてたSendMessageメソッドと、

サーバから呼び出してもらうCatchTalkメソッドを追加した

 

SendMessageメソッドではサーバのCatchMessageメソッドに

引数のメッセージを送ってもらいます

 

CatchTalkメソッドではサーバから受け取ったメッセージを

ClientFormクラスのWriteTalkメソッドに渡して

画面のリッチテキストボックスに表示してもらいます

 

サーバから呼び出したいメソッド「CatchTalk」については

上記のようにproxy.Onでメソッドを登録しておくとサーバが見つけてくれます

 

サーバ側のプロジェクト 

画面は変わってません

 

 Program.cs※前と変わりなし

using System.Windows.Forms;

namespace SignalRServerTest
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            ServerForm form = new ServerForm();
            FormSet.form = form;
            Application.Run(form);
        }
    }
}

 

 Startup.cs※前と変わりなし

using Microsoft.AspNet.SignalR;
using Owin;

namespace SignalRServerTest
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR(new HubConfiguration());
        }
    }
}

 

 FormSet.cs※前とほぼ変わりなし

using System.Collections.Generic;

namespace SignalRServerTest
{
    public static class FormSet
    {
        /// <summary>
        /// フォーム
        /// </summary>
        public static ServerForm form;

        /// <summary>
        /// 接続中クライアント一覧
        /// </summary>
        public static Dictionary<string, string> clients = new Dictionary<string, string>();
    }
}

 

 SignalRHost.cs※前とほぼ変わりなし

using Microsoft.Owin.Hosting;
using System;

namespace SignalRServerTest
{
    class SignalRHost
    {
        public static SignalRHost signalRHost = new SignalRHost();
        //private readonly IHubContext ihubcontext;
        private IDisposable server;

        /// <summary>
        /// 開始
        /// </summary>
        public void Start()
        {
            server = WebApp.Start<Startup>("http://localhost:8088");
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            server?.Dispose();
        }
    }
}

 

 SignalRServerTest.cs※前と変わりなし

using System;
using System.Windows.Forms;

namespace SignalRServerTest
{
    public partial class ServerForm : Form
    {
        private SignalRHost server;

        public ServerForm()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 開始ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenButton_Click(object sender, EventArgs e)
        {
            server = SignalRHost.signalRHost;
            server.Start();
        }

        /// <summary>
        /// フォームを閉じる
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ServerForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            server?.Stop();
        }

        /// <summary>
        /// メッセージ表示
        /// </summary>
        /// <param name="msg"></param>
        public void ShowMessage(string msg)
        {
            MessageTextBox.Text += msg + "\r\n";
        }
    }
}

 

 

 TestHub.cs

using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace SignalRServerTest
{
    public class TestHub : Hub
    {

        public void AnnounceSendName()
        {
            FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), 
                "名前が送信されました");
        }

        public void GetName(string name)
        {
            FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), 
                "名前:" + name);
            FormSet.clients.Add(Context.ConnectionId, name); 
        }

        public List<string> SendOurNames()
        {
            List<string> names = new List<string>(FormSet.clients.Values);
            return names;
        }

        public bool ExitChatRoom(string name)
        {
            if (FormSet.clients[Context.ConnectionId] == name)
            {
                FormSet.clients.Remove(Context.ConnectionId);
                FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), 
                    name + "さんが退出しました。");
                return true;
            }
            else
            {
                FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), 
                    name + "さんの退出処理に失敗しました。");
                return false;
            }
        }

        public void CatchMessage(string msg)
        {
            string name = FormSet.clients[Context.ConnectionId];
            FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), 
                name + "が発言しました。");
            FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), msg);
            Clients.All.CatchTalk(name + ":" + msg);
        }


        public override Task OnConnected()
        {
            object a = FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), Context.ConnectionId + "が接続しました");
            return base.OnConnected();
        }
        public override Task OnDisconnected(bool stopCalled)
        {
            FormSet.form.Invoke(new Action<string>(FormSet.form.ShowMessage), Context.ConnectionId + "が切断しました");
            return base.OnDisconnected(stopCalled);
        }
    }
}

HubクラスにCatchMessageメソッドを追加

この人はクライアントで「発言」ボタンを押されたときに呼ばれる人です

 

処理内容としては、CatchMessageを呼び出した人の接続IDから名前を割り出し

受け取ったメッセージにその名前をつけ足して

全ての接続中クライアントのCatchTalkメソッドに渡してくれます

クライアントの誰かが発言した内容をクライアント全員に配ってくれる感じです

 

動かしてみる 

まず前回までに作った処理を済ませてしまいます

サーバを開始してクライアント2人を接続、名前の送信を済ませておきます

 

左のクライアント「いぬ」のTalkTextBoxに発言内容を入力して

「発言」ボタンをクリックしてみます

 

「いぬ」の画面にも、右のクライアント「ねこ」の画面にも

いぬの発言が表示されました

ちゃんと受け取ったメッセージをすべてのクライアントに送ってくれてるみたいです

 

次は「ねこ」に発言させてみます

 

「いぬ」にも「ねこ」にも発言が表示されているので

ちゃんと受け取れているようです

 

サーバがクライアントのメソッドを呼び出すメモ 

【クライアント側の準備】クライアントのIHubProxyに、サーバが呼び出していいメソッドを登録

パラメータを受け取らない場合:

 proxy.On("メソッド名", メソッド);

パラメータを受け取る場合:

 proxy.On<引数の型>("メソッド名", メソッド);

※proxy:IHubProxy

 

【サーバ側の準備】※以下は全ての接続中クライアントのメソッドが呼ばれる

パラメータを送らない場合:

 Clients.All.メソッド名();

パラメータを送る場合:
 Clients.All.メソッド名(引数);