LoginSignup
3
7

More than 1 year has passed since last update.

.NET MAUIとSignalRでリアルタイム通信

Posted at

はじめに

今回は.NET MAUIでASP.NETのSignalRというリアルタイム接続をサポートする機能を活用したチャットアプリを作っていきます。

ASP.NETのテンプレートからSignalRのサーバーアプリを作成する。

ASP.NETのMVCテンプレートからSignalR用のサーバーアプリを作ります。

テンプレート.png
使用するフレームワークは.NET6を選択します。(.NET7では私の環境だとエラーが出ました)

program.csには以下のようにSignalRをserviceに追加し、SignalRのハブを追加します。

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

//↓SignalRの追加
builder.Services.AddSignalR();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
    
    //↓デバッグ(開発段階)ではこのメソッドを実行しない
    app.UseHttpsRedirection();
}
//↓今回デバッグではHTTPS接続を使用しないため、↑へ移動した
//app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

//↓MapHuでChatHubを追加、()の中はエンドポイント
app.MapHub<ChatHub>("/chat");

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

次にMapHubで追加させるChatHubクラスを作成する。

using Microsoft.AspNetCore.SignalR;

namespace SignalRdemo
{
    //Hubクラスを継承させる
    public class ChatHub:Hub
    {
        //↓SendMessageメソッドの引数はユーザー名とメッセージの内容とする
        public async void SendMessage(string user,string message)
        {
            //↓すべての接続されているクライアントに対して"ReceiveMessage"APIを通して送付する
            await Clients.All.SendAsync("ReceiveMessage",user,message);
        }
    }
}

そしてサーバーアプリをローカルホストではなく、実行したPCのIPアドレスで実行させるためにPropetiesの中に入っているlaunchsetting.jsonを変更する

{
 ~
  "profiles": {
    "SignalRdemo": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      //↓localhostを0.0.0.0に変更すると立ち上げたときPCのIPでサーバーを構築できる
      "applicationUrl": "https://0.0.0.0:7281;http://0.0.0.0:5103",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
  ~
  }
}

.NET MAUI側でチャット機能の実装

次に.NET MAUI側でチャット機能を実装させる
MainPage.xamlではサンプルのScrollViewを削除し、以下のグリッドを追加する

    <Grid RowDefinitions="Auto,7*,Auto,Auto"//←グリッドを4つに分割する。2番目の7*前の領域の7倍という意味
          Margin="8">
        <!--ユーザー名を記入するエントリー-->
        <Entry x:Name="userEntry"
               Placeholder="Your Name"/>
        <!--チャットを表示する部分ー-->
        <ScrollView Grid.Row="1">
            <Label x:Name="lblChat"
                   FontSize="14"
                   HorizontalOptions="StartAndExpand"/>
        </ScrollView>
        <!--メッセージを入力するエントリー-->
        <Entry x:Name="yourMessage"
               Grid.Row="2"
               Placeholder="Entry your message"/>
        <!--送信ボタン-->
        <Button x:Name="SendMessage"
                Grid.Row="3"
                Text="SendMessage"
                HorizontalOptions="FillAndExpand"
                Clicked="SendMessage_Clicked"/>
    </Grid>

コードビハインドでチャット機能を実装させる
まず、SignalRを使用するため以下のパッケージをNuGetする
Microsoft.AspNetCore.SignalR.Client(←プログラムが.NET6なので.NET6で一番最新のものを追加した)

using Microsoft.AspNetCore.SignalR.Client;//←名前空間を記入

namespace SignalRdemoClient
{
    public partial class MainPage : ContentPage
    {
        //↓SignalRのHubConnectionを読み込む
        private readonly HubConnection hubConnection;
        public MainPage()
        {
            InitializeComponent();
            //↓自分のPCのIPアドレスとlaunchsetting.jsonで書かれていたhttp://のポート番号を書く
            var baseUrl = "http://(自分のPCのIPアドレス(ex:123.45.67.89)):5103";

            //SignalRと接続するための準備
            hubConnection=new HubConnectionBuilder()
                .WithUrl($"{baseUrl}/chat")//←先ほどのURLの後にエンドポイント("/chat")を追加する
                .Build();

            //↓受け取ったAPIからユーザー名とメッセージを反映する↓ChatHubで作成したAPIと一緒の名前
            hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
            {
                //受け取ったメッセージに改行文字を加えたものをどんどんラベルに追加していく
                lblChat.Text += $"{user}:{message}{Environment.NewLine}";
            });

            Task.Run(() =>
            {
                //↓SignalRと接続している
                Dispatcher.Dispatch(async () =>
                {
                    await hubConnection.StartAsync();
                });
            });
        }

        private async void SendMessage_Clicked(object sender, EventArgs e)
        {
            //↓入力したエントリーの文字を配列にしてサーバー側のSendMessageメソッドに渡す。
            await hubConnection.InvokeCoreAsync("SendMessage", args: new[]
            {
                userEntry.Text,
                yourMessage.Text,
            });
            //↓メッセージを送付したら入力していたメッセージ欄の文字を削除する
            yourMessage.Text= String.Empty;
        }
    }
}

各プラットフォームにはAPIと通信するための権限が必要となります。
Androidでは以下のようにAndroidManifest.xmlに追加します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
	<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"
                 android:usesCleartextTraffic="true">//←applicationにandroid:usesCleartextTrafficを追加してtrueとする
    </application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.INTERNET" />
</manifest>

デバッグについて

このアプリは最初にサーバーが機能していないと使えないので、サーバーアプリの方から先に立ち上げる必要があります。
ソリューションエクスプローラーからサーバープロジェクトを右クリック、デバッグ→デバッグなしで開始とするとサーバーアプリが立ち上がります。
デバッグモードにならないため、いつも通り、.NET MAUIアプリをデバッグすることができます。
デバッグ方法.png

さいごに

この方法でPC内のWindowsアプリ同士でSignalR接続はできたのですが、ローカルAndroidで送信ボタンを押すと以下のようなエラーが出ました。

failed to connect to /{PC側のIPアドレス} (port 5103) from /{Android側のIPアドレス} (port 46710) after 86400000ms: isConnected failed: ETIMEDOUT (Connection timed out)

PC側のファイアーウォールで設定しないといけないかもしれません。
だれかわかる人いたらコメント欄で教えてください。

3
7
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
3
7