ASP.NET Core SignalR .NET クライアント ライブラリを使用すると、.NET アプリから SignalR ハブと通信できます。 この記事では、API を使用して SignalR ハブに接続し、.NET ハブとクライアント メソッドを呼び出す方法について説明します。 この記事のコード サンプルは、ASP.NET Core SignalR .NET クライアントを使用するWindows Presentation Foundation (WPF) アプリです。
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
SignalR.NET クライアント パッケージをインストールする
.NET クライアントが SignalR ハブに接続するには、SignalR パッケージが必要です。 クライアント ライブラリは、Visual Studio パッケージ マネージャー コンソールまたは .NET CLI を使用してインストールできます。
パッケージ マネージャー Console ウィンドウで次のコマンドを実行します。
Install-Package Microsoft.AspNetCore.SignalR.Client
ハブに接続する
接続を確立するには、HubConnectionBuilder を作成して、Build を呼び出します。 ハブ URL、プロトコル、トランスポートの種類、ログ レベル、ヘッダー、その他のオプションは、接続を確立する間に構成できます。 必要なオプションを構成するには、HubConnectionBuilder メソッドのいずれかを Build に挿入します。
StartAsync を使って接続を開始します。
using System;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.AspNetCore.SignalR.Client;
namespace SignalRChatClient
{
public partial class MainWindow : Window
{
HubConnection connection;
public MainWindow()
{
InitializeComponent();
connection = new HubConnectionBuilder()
.WithUrl("http://localhost:53353/ChatHub")
.Build();
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0,5) * 1000);
await connection.StartAsync();
};
}
private async void connectButton_Click(object sender, RoutedEventArgs e)
{
connection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Dispatcher.Invoke(() =>
{
var newMessage = $"{user}: {message}";
messagesList.Items.Add(newMessage);
});
});
try
{
await connection.StartAsync();
messagesList.Items.Add("Connection started");
connectButton.IsEnabled = false;
sendButton.IsEnabled = true;
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
}
private async void sendButton_Click(object sender, RoutedEventArgs e)
{
try
{
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
}
}
}
失われた接続を処理する
クライアントと再接続するには、自動再接続を設定するか、手動で再接続を構成します。
自動的に再接続する
HubConnectionは、WithAutomaticReconnectの HubConnectionBuilder メソッドを使用して自動的に再接続するように構成できます。 既定では自動的に再接続されません。
HubConnection connection= new HubConnectionBuilder()
.WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
.WithAutomaticReconnect()
.Build();
パラメーターを指定せずに、 WithAutomaticReconnect() は、再接続の試行を試行する前に、それぞれ 0、2、10、および 30 秒待機するようにクライアントを構成します。 4 回試行が失敗した後に停止します。
再接続の試行を開始する前に、 HubConnection は HubConnectionState.Reconnecting 状態に遷移し、 Reconnecting イベントを発生させます。 この方法では、接続が失われることをユーザーに警告し、UI 要素を無効にする機会が提供されます。 非対話型アプリでは、メッセージのキューイングまたは削除を開始できます。
connection.Reconnecting += error =>
{
Debug.Assert(connection.State == HubConnectionState.Reconnecting);
// Notify users the connection was lost and the client is reconnecting.
// Start queuing or dropping messages.
return Task.CompletedTask;
};
クライアントが最初の 4 回の試行で正常に再接続されると、 HubConnection は Connected 状態に戻り、 Reconnected イベントを発生させます。 この方法では、接続が再確立されたことをユーザーに通知し、キューに入っているメッセージをデキューする機会を提供します。
接続はサーバーにまったく新しく見えるので、ConnectionId イベント ハンドラーに新しいReconnectedが提供されます。
connection.Reconnected += connectionId =>
{
Debug.Assert(connection.State == HubConnectionState.Connected);
// Notify users the connection was reestablished.
// Start dequeuing messages queued while reconnecting if any.
return Task.CompletedTask;
};
WithAutomaticReconnect() では、最初の開始エラーを再試行するように HubConnection が構成されていないため、開始エラーは手動で処理する必要があります。
public static async Task<bool> ConnectWithRetryAsync(HubConnection connection, CancellationToken token)
{
// Keep trying to until we can start or the token is canceled.
while (true)
{
try
{
await connection.StartAsync(token);
Debug.Assert(connection.State == HubConnectionState.Connected);
return true;
}
catch when (token.IsCancellationRequested)
{
return false;
}
catch
{
// Failed to connect, trying again in 5000 ms.
Debug.Assert(connection.State == HubConnectionState.Disconnected);
await Task.Delay(5000);
}
}
}
クライアントが最初の 4 回の試行で正常に再接続しなかった場合、 HubConnection は Disconnected 状態に遷移し、 Closed イベントを発生させます。 この方法では、接続を手動で再起動するか、接続が完全に失われたとユーザーに通知する機会が提供されます。
connection.Closed += error =>
{
Debug.Assert(connection.State == HubConnectionState.Disconnected);
// Notify users the connection has been closed or manually try to restart the connection.
return Task.CompletedTask;
};
再接続のタイミングを切断または変更する前に、カスタムの再接続試行回数を構成するために、 WithAutomaticReconnect は、各再接続試行を開始する前に待機するミリ秒単位の遅延を表す数値の配列を受け入れます。
HubConnection connection = new HubConnectionBuilder()
.WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
.WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.Zero, TimeSpan.FromSeconds(10) })
.Build();
// .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30) }) yields the default behavior.
上の例では、接続が失われたらすぐに再接続を試み始めるよう、HubConnection を構成しています。 この方法は、既定の構成にも当てはまります。
最初の再接続の試行が失敗した場合、既定の構成で定義されている 2 秒待たずに、2 回目の再接続の試行もすぐに開始されます。
2 回目の再接続試行が失敗した場合、3 回目の再接続試行は 10 秒で開始されます。これは、既定の構成で定義されているのと同じ動作です。
その後のカスタム動作は、再び既定の動作とは異なり、3 回目の再接続の試行が失敗したら停止します。 既定の構成では、30 秒後にもう 1 回再接続が試行されます。
自動再接続試行のタイミングと回数をさらに細かく制御したい場合は、WithAutomaticReconnect は IRetryPolicy インターフェイスが実装されているオブジェクトを受け取ります。このインターフェイスには、NextRetryDelay という名前のメソッドが 1 つ含まれます。
NextRetryDelay は、RetryContext 型の引数を 1 つ受け取ります。
RetryContextには、PreviousRetryCount (型long)、ElapsedTime (型TimeSpan)、RetryReason (型Exception) の 3 つのプロパティがあります。
最初の再接続を試みる前に、
PreviousRetryCountとElapsedTimeはどちらもゼロ (0) であり、RetryReasonは接続が失われた原因となった例外です。再試行が失敗するたびに、
PreviousRetryCountが 1 ずつインクリメントされ、これまでに再接続に費やされた時間を反映するように更新ElapsedTime、最後の再接続試行が失敗する原因となった例外RetryReason。
NextRetryDelayは、次の再接続の試行までの待機時間を表すTimeSpan値を返すか、nullが再接続を停止する必要がある場合はHubConnectionする必要があります。
public class RandomRetryPolicy : IRetryPolicy
{
private readonly Random _random = new Random();
public TimeSpan? NextRetryDelay(RetryContext retryContext)
{
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60))
{
return TimeSpan.FromSeconds(_random.NextDouble() * 10);
}
else
{
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
}
HubConnection connection = new HubConnectionBuilder()
.WithUrl(new Uri("http://127.0.0.1:5000/chathub"))
.WithAutomaticReconnect(new RandomRetryPolicy())
.Build();
または、次のセクションで示すように、クライアントを手動で再接続するコードを記述することもできます。
手動で再接続する
Warning
3.0 より前のバージョンでは、SignalR の.NET クライアントは自動的に再接続されません。 クライアントを手動で再接続するコードを記述する必要があります。
失われた接続に応答するには、Closed イベントを使います。 たとえば、再接続を自動化することが必要な場合があります。
Closed イベントには、Task を返すデリゲートが必要です。これにより、async void を使用せずに 非同期コードを実行できます。 同期的に実行される Closed イベント ハンドラーでデリゲート シグネチャを満たすには、Task.CompletedTask を返します。
connection.Closed += (error) => {
// Do your close logic.
return Task.CompletedTask;
};
非同期をサポートする主な理由は、接続を開始し直すことができるためです。 接続の開始は非同期アクションです。
接続を開始し直す Closed ハンドラーでは、サーバーが過負荷になるのを防ぐため、次の例に示すように、ランダムな遅延時間だけ待機することを検討します。
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0,5) * 1000);
await connection.StartAsync();
};
クライアントからハブ メソッドを呼び出す
InvokeAsync はハブのメソッドを呼び出します。 ハブ メソッド名と、ハブ メソッドに定義されている引数を InvokeAsync に渡します。
SignalR は非同期なので、呼び出しを行うときに async と await を使います。
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
InvokeAsync メソッドは、サーバー メソッドが戻ったときに完了するTaskを返します。 戻り値がある場合は、Task の結果として提供されます。 サーバーのメソッドによって例外がスローされると、エラーのある Task が生成されます。 サーバーのメソッドが完了するのを待機するには await 構文を使い、エラーを処理するには try...catch を使います。
SendAsync メソッドは、メッセージがサーバーに送信されたときに完了するTaskを返します。 この Task はサーバー メソッドが完了するまで待機しないため、戻り値は指定されません。 メッセージの送信中にクライアントで例外がスローされると、エラーのある Task が生成されます。 メッセージ送信エラーを処理するには、 await 構文と try...catch 構文を使用します。
Note
クライアントからのハブ メソッドの呼び出しは、SignalR モードで Azure サービスを使用する場合にのみサポートされます。 詳細については、「 頻度の低い質問」を参照してください。
ハブからクライアントのメソッドを呼び出す
ビルド後に接続を開始する前に、 connection.On を使用してハブが呼び出すメソッドを定義します。
connection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Dispatcher.Invoke(() =>
{
var newMessage = $"{user}: {message}";
messagesList.Items.Add(newMessage);
});
});
connection.Onの上記のコードは、SendAsync メソッドを使用してサーバー側のコードによって呼び出されたときに実行されます。
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Note
接続のハブ側では厳密に型指定されたメッセージングがサポートされていますが、クライアントは、メソッド名で HubConnection.On ジェネリック メソッドを使用して登録する必要があります。 例については、「バックグラウンド サービスで ASP.NET Core SignalR をホストする」を参照してください。
エラー処理とログ記録
try...catch ステートメントでエラーを処理します。
Exception オブジェクトを調べて、エラーが発生した後に実行する適切なアクションを判断します。
try
{
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
再接続せずに認証を更新する
サーバーが認証更新を有効にすると、.NET クライアントは、アクセス トークンの有効期限が切れたときに接続を閉じるのではなく、開いている接続の資格情報を置き換えることができます。 自動更新は既定で有効になっており、 HubConnection は即時更新を要求する RefreshAuthenticationAsync メソッドを公開します。 更新のタイミングを変更したり、自動更新をオフにしたりするには、接続を構築するときに WithAuthenticationRefresh を呼び出します。 クライアント オプション、更新イベント、および機能を有効にするサーバー オプションについては、 SignalR 認証と承認に関するページを参照してください。
関連するコンテンツ
ASP.NET Core