ASP.NET Core 2026のSignalR完全ガイド:リアルタイム通信、Hub、面接対策
ASP.NET CoreにおけるSignalRでリアルタイムWebアプリケーションを構築する方法を解説。Hub、グループ、Redisによるスケーリング、技術面接頻出問題まで網羅。

SignalRは、ASP.NET Coreアプリケーションにおいてサーバーとクライアント間の双方向リアルタイム通信を実現するライブラリです。従来のHTTPリクエスト・レスポンスパターンとは異なり、SignalRは永続的な接続を維持し、サーバーからクライアントへ即座にデータをプッシュできます。チャットアプリケーション、ライブダッシュボード、共同編集ツール、リアルタイム通知システムなど、即時性が求められるアプリケーションに不可欠な技術です。
SignalRは利用可能な最適なトランスポートを自動選択します。まずWebSocketsを試行し、次にServer-Sent Events、最後にLong Pollingへフォールバックします。このメカニズムにより、コード変更なしで全てのブラウザとネットワーク構成に対応できます。
SignalR Hubによるサーバー・クライアント間通信の仕組み
Hubは、クライアントとサーバー間のメソッド呼び出しを処理する高レベルのパイプラインとして機能します。クライアントはHub上のメソッドを呼び出し、HubはConnected状態のクライアント上のメソッドを呼び出すことができます。この抽象化により、WebSocket接続を手動で管理する複雑さが解消されます。
以下の例は、接続中の全クライアントにメッセージをブロードキャストする基本的なチャットHubを示しています:
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
// Called when a client sends a message
public async Task SendMessage(string user, string message)
{
// Broadcast to ALL connected clients
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
// Called automatically when a client connects
public override async Task OnConnectedAsync()
{
await Clients.Caller.SendAsync("Connected", Context.ConnectionId);
await base.OnConnectedAsync();
}
// Called automatically when a client disconnects
public override async Task OnDisconnectedAsync(Exception? exception)
{
// Clean up resources, notify other users, etc.
await base.OnDisconnectedAsync(exception);
}
}Clientsプロパティは、All、Caller、Others、Group、Userなど様々なターゲティングオプションを通じて、全ての接続クライアントへのアクセスを提供します。
ASP.NET Core 9におけるSignalRの設定
ASP.NET Core 9でのSignalR登録は、標準的なミドルウェアパターンに従います。以下の設定では、JSONとMessagePackプロトコルをカスタムバッファサイズで有効にしています:
var builder = WebApplication.CreateBuilder(args);
// Add SignalR services with configuration
builder.Services.AddSignalR(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.MaximumReceiveMessageSize = 64 * 1024; // 64 KB
options.StreamBufferCapacity = 10;
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
})
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.PropertyNamingPolicy = null; // PascalCase
});
var app = builder.Build();
// Map the hub endpoint
app.MapHub<ChatHub>("/chathub");
app.Run();KeepAliveIntervalとClientTimeoutIntervalの設定は、接続の健全性監視を制御します。タイムアウト期間内に応答しないクライアントは切断されたものとみなされます。
グループを活用したターゲットメッセージ配信
グループは、接続を整理し、クライアントのサブセットにメッセージを送信するメカニズムを提供します。典型的なユースケースはチャットルームで、ユーザーが特定のルームに参加し、そのルームからのメッセージのみを受信します。
public class GroupChatHub : Hub
{
// Add the current connection to a group
public async Task JoinRoom(string roomName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
// Notify the group that someone joined
await Clients.Group(roomName).SendAsync(
"UserJoined",
Context.User?.Identity?.Name ?? "Anonymous"
);
}
// Remove the current connection from a group
public async Task LeaveRoom(string roomName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName).SendAsync(
"UserLeft",
Context.User?.Identity?.Name ?? "Anonymous"
);
}
// Send a message to a specific group only
public async Task SendToRoom(string roomName, string message)
{
await Clients.Group(roomName).SendAsync(
"ReceiveMessage",
Context.User?.Identity?.Name,
message
);
}
}グループメンバーシップは接続に紐づいており、ユーザーには紐づいていません。ユーザーが再接続した場合、グループに再参加する必要があります。永続的なグループメンバーシップを実現するには、グループの関連付けをデータベースに保存し、OnConnectedAsyncで自動的に再参加します。
グループはメモリ上にのみ存在し、サーバー再起動時やバックプレーンなしのスケールアウトシナリオでは永続化されません。複数サーバーを使用する本番デプロイメントでは、RedisまたはAzure SignalR Serviceをバックプレーンとして使用してください。
認証によるSignalR接続のセキュリティ保護
SignalRはASP.NET Core認証と統合されています。[Authorize]属性でHubアクセスを認証済みユーザーに制限し、Context.Userで認証済みユーザーのクレームにアクセスできます。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
[Authorize] // Require authentication for the entire hub
public class SecureHub : Hub
{
// Access user claims through Context.User
public async Task SendPrivateMessage(string recipientUserId, string message)
{
var senderName = Context.User?.Identity?.Name
?? throw new HubException("User not authenticated");
// Send to a specific user (all their connections)
await Clients.User(recipientUserId).SendAsync(
"PrivateMessage",
senderName,
message
);
}
[Authorize(Roles = "Admin")] // Role-based authorization on method
public async Task BroadcastAnnouncement(string announcement)
{
await Clients.All.SendAsync("Announcement", announcement);
}
}WebSocket接続では、カスタムヘッダーをサポートしていないため、認証トークンをクエリ文字列経由で渡す必要があります。JavaScriptクライアントでトークンを含める設定は以下の通りです:
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder()
.withUrl("/securehub", {
accessTokenFactory: () => localStorage.getItem("authToken") || ""
})
.withAutomaticReconnect()
.build();
await connection.start();サーバー側では、クエリ文字列からトークンを読み取るようJWT認証を設定します:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Read token from query string for SignalR
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments("/securehub"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});.NETの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
Redisバックプレーンによるスケーリング
ロードバランス環境では、クライアントが異なるサーバーインスタンスに接続する可能性があります。バックプレーンがない場合、あるサーバーから送信されたメッセージは、そのサーバーに接続しているクライアントにのみ届きます。Redisは、全サーバーインスタンス間でメッセージを同期するバックプレーンとして機能します。
builder.Services.AddSignalR()
.AddStackExchangeRedis(options =>
{
options.Configuration = builder.Configuration
.GetConnectionString("Redis");
// Optional: configure Redis-specific options
options.Configuration.ChannelPrefix =
RedisChannel.Literal("MyApp_SignalR_");
});Redisバックプレーンは、全てのSignalRメッセージをRedisチャネルにパブリッシュします。各サーバーインスタンスはこれらのチャネルをサブスクライブし、ローカルクライアントにメッセージを配信します。このアプローチでは最小限のレイテンシ(通常1〜2ms)で水平スケーリングが可能になります。
エンタープライズデプロイメントでは、Azure SignalR Serviceがスケーリング、接続管理、可用性を自動的に処理するマネージドな代替手段を提供します。
型安全なHubによるコンパイル時検証
強く型付けされたHubは、マジックストリングをインターフェースメソッドに置き換え、クライアントメソッド呼び出しのコンパイル時検証を可能にします:
public interface INotificationClient
{
Task ReceiveNotification(string title, string message, string severity);
Task UpdateProgress(int percentage);
Task TaskCompleted(Guid taskId, bool success);
}
// NotificationHub.cs
public class NotificationHub : Hub<INotificationClient>
{
public async Task NotifyAll(string title, string message)
{
// Compile-time checking - no magic strings
await Clients.All.ReceiveNotification(title, message, "info");
}
public async Task ReportProgress(string groupName, int percentage)
{
// IDE autocomplete works here
await Clients.Group(groupName).UpdateProgress(percentage);
}
}インターフェースのメソッド名を変更すると、更新が必要な全ての呼び出し箇所が即座に表示されるため、リファクタリングがより安全になります。
SignalRチャネルによるストリーミング
SignalRは、進捗更新、ログのテーリング、リアルタイムセンサーデータなど、データが段階的に生成されるシナリオ向けのストリーミングをサポートしています。サーバーからクライアント、クライアントからサーバーの両方向のストリーミングが可能です。
public class StreamingHub : Hub
{
// Server-to-client streaming with IAsyncEnumerable
public async IAsyncEnumerable<StockPrice> StreamStockPrices(
string[] symbols,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
var random = new Random();
while (!cancellationToken.IsCancellationRequested)
{
foreach (var symbol in symbols)
{
yield return new StockPrice
{
Symbol = symbol,
Price = random.NextDouble() * 1000,
Timestamp = DateTime.UtcNow
};
}
await Task.Delay(1000, cancellationToken);
}
}
// Client-to-server streaming
public async Task UploadStream(IAsyncEnumerable<LogEntry> stream)
{
await foreach (var entry in stream)
{
// Process each log entry as it arrives
await ProcessLogEntry(entry);
}
}
}
public record StockPrice(string Symbol, double Price, DateTime Timestamp);
public record LogEntry(string Level, string Message, DateTime Timestamp);JavaScriptクライアントは非同期イテレータを使用してストリームを消費します:
const stream = connection.stream("StreamStockPrices", ["AAPL", "MSFT"]);
stream.subscribe({
next: (price) => console.log(`${price.symbol}: $${price.price}`),
error: (err) => console.error(err),
complete: () => console.log("Stream completed")
});SignalR技術面接頻出問題
技術面接では、SignalRのアーキテクチャと実務シナリオが頻繁に取り上げられます。以下は.NET面接セッションで定期的に出題される問題です:
トランスポートネゴシエーションプロセスを説明できるようにしましょう:SignalRはまずWebSocketsを試行し、Server-Sent Events、Long Pollingの順にフォールバックします。各トランスポートがいつ使用され、その制限は何かを理解しておくことが重要です。
Q: クライアントが一時的に接続を失った場合、SignalRはどのように接続状態を処理しますか?
SignalRは設定可能な期間、サーバー上で接続状態を維持します。自動再接続が有効な場合、クライアントは同じ接続IDを使用して再接続を試みます。成功すれば、グループメンバーシップを失わずに接続が再開されます。切断がタイムアウトを超えた場合は、新しい接続が確立され、クライアントはグループに再参加する必要があります。
Q: Clients.User()とClients.Client()の違いは何ですか?
Clients.User(userId)は、特定の認証済みユーザーに属する全ての接続をターゲットにします。これは、1人のユーザーが複数のブラウザタブを開いている場合に便利です。Clients.Client(connectionId)は、単一の特定の接続をターゲットにします。ユーザーベースのターゲティングには認証が必要で、IUserIdProviderを使用して接続をユーザーIDにマッピングします。
Q: プレゼンス検出(オンラインユーザーの表示)をどのように実装しますか?
OnConnectedAsyncとOnDisconnectedAsyncで接続を追跡します。ユーザーと接続のマッピングをConcurrentDictionaryまたは分散キャッシュに保存します。スケールアウトされたデプロイメントでは、Redisなどの共有ストアを使用します。ユーザーの接続または切断時に、関連するグループにプレゼンス更新をブロードキャストします。
Q: SignalRバックプレーンの目的を説明してください。
バックプレーンは、ロードバランス環境における複数のサーバーインスタンス間でメッセージを同期します。バックプレーンがない場合、サーバーAから送信されたメッセージは、サーバーAに接続しているクライアントにのみ届きます。RedisとAzure SignalR Serviceが一般的なバックプレーンオプションです。バックプレーンは、全サーバーがサブスクライブする共有チャネルにメッセージをパブリッシュします。
パフォーマンス最適化戦略
SignalRのパフォーマンスは、メッセージサイズ、頻度、接続数に依存します。ASP.NET Coreパフォーマンスベストプラクティスのドキュメントでは、詳細なベンチマークが提供されています。
- MessagePackプロトコルを使用:バイナリシリアライゼーションにより、JSONと比較してペイロードサイズを30〜50%削減
- メッセージをバッチ処理:多数の小さなメッセージを送信する代わりに、可能な限りまとめる
- グループサイズを制限:メンバー数は数千に抑える。より大規模な配信にはpub/subパターンを検討
- 圧縮を有効化:テキスト中心のペイロードに対して有効
- 適切なタイムアウトを設定:古い接続からのリソースを速やかに解放
// MessagePack configuration for smaller payloads
builder.Services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializerOptions =
MessagePackSerializerOptions.Standard
.WithCompression(MessagePackCompression.Lz4Block);
});まとめ
- SignalRはトランスポート選択(WebSockets、SSE、Long Polling)を抽象化し、リアルタイム通信のための一貫したプログラミングモデルを提供します
- Hubは接続管理を一元化し、強く型付けされたHubはクライアントメソッド呼び出しにコンパイル時の安全性を追加します
- グループは接続のサブセットへのターゲットメッセージ配信を可能にします。再接続後はグループへの再参加が必要です
- 認証はASP.NET Coreアイデンティティと統合されます。WebSocket接続ではクエリ文字列経由でトークンを渡します
- Redisバックプレーンまたは Azure SignalR Serviceを使用して、サーバーインスタンス間でメッセージを同期し、水平スケーリングを実現します
- ストリーミングは
IAsyncEnumerableを使用した段階的なデータ配信をサポートし、サーバーからクライアント、クライアントからサーバーの両方向で利用可能です
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。

執筆
Anthony Fillion-Mailletフルスタック開発者、SharpSkill 創業者
10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。
2026年8月12日 更新
共有
関連記事

ASP.NET Core Minimal APIs 2026年完全ガイド:アーキテクチャ、パフォーマンス、面接対策
ASP.NET Core Minimal APIsの設計原則、パフォーマンス最適化、依存性注入、エンドポイント整理の実践的解説。2026年の技術面接で頻出する質問と回答例も収録。

.NET 10(2026年版):新機能、Native AOT、面接対策完全ガイド
.NET 10の新機能を徹底解説。Native AOTの本番環境対応、C# 14の拡張メンバー、fieldキーワード、EF Core 10の名前付きクエリフィルターなど、面接で差をつける知識を網羅します。

.NET MAUI 2026年完全ガイド:クロスプラットフォーム開発と面接頻出質問
.NET MAUI 10によるクロスプラットフォームアプリ開発の手順をHandlers、MVVM、HybridWebView、SafeAreaEdgesと共に解説し、2026年の技術面接で頻出する質問と回答を体系的にまとめています。