ASP.NET CoreとSignalRによるリアルタイム通信
1. この記事の範囲
SignalRは、ASP.NET Coreに含まれるライブラリで、ウェブアプリにおけるリアルタイム通信を簡単に実現できます。この記事では、SignalRを使用したチャットアプリのサンプルコードを紹介し、SignalRの仕組みや技術的なポイントについても解説します。
2. SignalRとは
特徴
SignalRは、クライアントとサーバー間のリアルタイム通信を実現するライブラリです。その主な特徴は、下記のような持続的な接続モデルを自動的に選択する点です。
-
WebSockets:
- 高速で効率的なプロトコル。クライアントとサーバー間の双方向通信を実現します。
-
Server-Sent Events (SSE):
- HTTPプロトコルを利用して、サーバーからクライアントへの一方向通信を可能にします。
-
Long Polling:
- WebSocketsやSSEが利用できない場合に、HTTPリクエストを繰り返すことで通信を維持します。
SignalRの仕組み
SignalRは、これらのプロトコルを自動的に選択することで、開発者の負担を軽減します。具体的には、以下のプロセスを通じてプロトコルを決定します。
-
ネゴシエーションフェーズ:
- クライアントがサーバーに接続リクエストを送信し、利用可能なプロトコルをネゴシエートします。例えば、WebSocketsがサポートされている場合は優先的に使用し、サポートされていない場合はSSE、それも不可能な場合はLong Pollingにフォールバックします。
-
接続の維持:
- SignalRは接続の中断を検出し、再接続を自動的に試みます(例: ネットワークの一時的な障害時)。
- 再接続に成功すれば、途切れた通信を復元します。
-
メッセージの送受信:
- サーバーからクライアント、またはクライアントからサーバーへのメッセージ送信は、JSONまたはMessagePack形式でシリアライズされます。
- MessagePack形式を使用すると、データ量を抑え、パフォーマンスが向上します。
SignalRでの双方向通信
SignalRは、サーバーとクライアント間の双方向通信を実現するため、以下の機能を提供します。
-
ブロードキャスト:
- サーバーからすべての接続中のクライアントにメッセージを送信します。
- 例: チャットアプリでの「全員に通知」機能。
-
グループ通信:
- クライアントをグループに分類し、特定のグループにだけメッセージを送信します。
- 例: チャットルームでの「特定のルーム内でのメッセージ送信」。
-
個別通信:
- 特定のクライアントにのみメッセージを送信します(Connection IDを使用して特定)。
- 例: プライベートチャット機能。
3. サンプル実装
3-1. サンプル概要
このサンプル実装では、SignalRを利用して以下の機能を持つシンプルなリアルタイムチャットアプリを構築します。
-
メッセージの送信と受信
- クライアント間でリアルタイムにメッセージを送受信できます。
- 個別通信をサポートしています。
-
メンバーリスト
- 現在オンラインのユーザーを表示し、クリックすることでチャット相手を選択できます。
-
簡潔なUI/UX
- メンバーリストとチャットエリアを分けた直感的なレイアウトを提供します。
-
SignalRの自動フォールバック機能を活用
- 環境に応じて最適なプロトコルを選択し、安定した通信を実現します。
3-2. 実装構成
構成
-
バックエンド
- ASP.NET Coreを使用してSignalRハブを構築し、クライアント間の通信を管理します。
-
フロントエンド
- JavaScriptを使用してSignalRクライアントライブラリを活用し、サーバーと通信します。
- HTMLとCSSを利用してシンプルかつ直感的なチャットUIを構築します。
SignalRを利用する理由
SignalRは、リアルタイム通信を簡単に実現するためのライブラリです。このサンプル実装では、以下の理由からSignalRを採用しています。
-
双方向通信を簡単に実現
SignalRを使用することで、サーバーとクライアント間の双方向通信をリアルタイムで行えます。これにより、チャットアプリケーションのようなインタラクティブな機能を効率的に構築できます。 -
プロトコルの自動選択
SignalRは、WebSocketを優先的に使用し、利用できない場合はSSEやLong Pollingにフォールバックします。これにより、環境に依存せず安定した通信が可能です。 -
グループ管理機能
SignalRでは、クライアントをグループごとに管理することができます。これにより、特定のユーザー間やグループ内で効率的なメッセージルーティングが可能です。たとえば、チャットルームやプライベートチャットの実装に役立ちます。
3-3. コード解説
3-3-1. SignalRクライアントライブラリの追加
SignalRサーバーライブラリは、ASP.NET Core共有フレームワークに含まれていますが、JavaScriptクライアントライブラリはプロジェクトに自動的に含まれません。以下の手順でクライアントライブラリを追加します。
-
ライブラリマネージャー (LibMan) を使用して、unpkg からクライアントライブラリを取得します。
libman install @microsoft/signalr --provider unpkg
- クライアントライブラリがインストールされると、プロジェクト内で使用可能になります。このライブラリを使ってSignalRサーバーと通信します。
3-3-2. SignalR サーバーの構成
SignalR要求が正しく処理されるように、ASP.NET CoreアプリケーションでSignalRを構成する必要があります。以下のコードをProgram.cs
ファイルに追加して設定を行います。
using SignalRChat.Hubs;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
-
builder.Services.AddSignalR();
:- SignalRサービスをASP.NET Coreの依存関係挿入システムに追加します。
-
app.MapHub<ChatHub>("/chatHub");
:- SignalRハブのエンドポイントを定義します。クライアントは
/chatHub
を介してサーバーと通信します。
- SignalRハブのエンドポイントを定義します。クライアントは
この設定により、SignalRがASP.NET Coreのルーティングシステムに統合され、リアルタイム通信が可能になります。
3-3-3. SignalR ハブの作成
ハブは、クライアントとサーバー間の通信を処理するためのパイプラインです。以下の手順でハブを作成します。
-
Hubs フォルダーを作成: プロジェクトフォルダー内に
Hubs
フォルダーを作成します。 -
ChatHub クラスの作成: SignalRの
Hub
クラスを継承するChatHub
クラスをHubsフォルダーに作成します。
以下はChatHub
クラスのコード例です。
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
public class ChatHub : Hub
{
public async Task SendMessage(string sender, string receiver, string message)
{
// メッセージ送信ログ
Console.WriteLine($"[SendMessage] {sender} -> {receiver}: {message}");
// 受信者にのみメッセージを送信
await Clients.Group(receiver).SendAsync("ReceiveMessage", sender, message);
}
public override async Task OnConnectedAsync()
{
var username = Context.GetHttpContext()?.Request.Query["username"];
if (!string.IsNullOrEmpty(username))
{
// ユーザーをグループに追加
Console.WriteLine($"[OnConnected] User connected: {username}");
await Groups.AddToGroupAsync(Context.ConnectionId, username);
}
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
var username = Context.GetHttpContext()?.Request.Query["username"];
if (!string.IsNullOrEmpty(username))
{
// ユーザーをグループから削除
Console.WriteLine($"[OnDisconnected] User disconnected: {username}");
await Groups.RemoveFromGroupAsync(Context.ConnectionId, username);
}
await base.OnDisconnectedAsync(exception);
}
}
-
SendMessage
:- 送信者から受信者へメッセージを送信するメソッドです。
- 特定の受信者にのみメッセージを送るため、
Clients.Group
を使用します。
-
OnConnectedAsync
:- クライアントが接続した際に実行されるメソッドです。
- ユーザーを特定のグループに追加します。
-
OnDisconnectedAsync
:- クライアントが切断した際に実行されるメソッドです。
- ユーザーをグループから削除します。
3-3-4. クライアントサイドの実装
クライアントサイドでは、JavaScriptを使用してSignalRのサーバーと接続します。以下は、クライアントの基本的なコードです。
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub?username=YourUsername")
.build();
connection.on("ReceiveMessage", (sender, message) => {
console.log(`Message from ${sender}: ${message}`);
});
connection.start()
.then(() => console.log("Connected to SignalR server."))
.catch(err => console.error("Connection error:", err));
function sendMessage(receiver, message) {
connection.invoke("SendMessage", "YourUsername", receiver, message)
.catch(err => console.error("Send error:", err));
}
クライアントの動作
-
接続確立:
- クライアントがサーバーに接続し、自身のユーザー名を送信します。
- サーバーは、ユーザー名をグループとして登録し、メッセージ送信先として使用します。
-
メッセージ送信:
- クライアントが送信したメッセージは、サーバーを経由して指定された受信者のグループに送信されます。
-
メッセージ受信:
- クライアントは
ReceiveMessage
イベントをリッスンし、リアルタイムで受信したメッセージを画面に表示します。
- クライアントは
4. まとめ
SignalRは、リアルタイム通信を必要とするアプリケーションの基盤を効率的に構築するための強力なツールです。その柔軟性と自動プロトコル選択機能により、幅広いアプリケーションへの適用が可能です。SignalRを利用することで、複雑なリアルタイム通信のロジックを簡略化し、開発にかかる時間とコストを大幅に削減できます。また、グループ管理やプロトコル選択機能を活用することで、アプリケーションの規模や使用シナリオに応じた柔軟で拡張性のある設計を実現できます。
Discussion