ASP.NET SignalR Hubs API ガイド - サーバー (SignalR 1.x)

作成者: Patrick FletcherTom Dykstra

警告

このドキュメントは、SignalR の最新バージョン用ではありません。 SignalR の ASP.NET Coreを見てみましょう。

このドキュメントでは、SignalR バージョン 1.1 用の ASP.NET SignalR Hubs API のサーバー側のプログラミングの概要と、一般的なオプションを示すコード サンプルについて説明します。

SignalR Hubs API を使用すると、サーバーから接続されているクライアント、およびクライアントからサーバーへのリモート プロシージャ コール (RPC) を行うことができます。 サーバー コードでは、クライアントが呼び出すことができるメソッドを定義し、クライアントで実行されるメソッドを呼び出します。 クライアント コードでは、サーバーから呼び出すことができるメソッドを定義し、サーバー上で実行されるメソッドを呼び出します。 SignalR は、すべてのクライアントとサーバーの組み合てを処理します。

SignalR には、永続的な接続と呼ばれる下位レベルの API も用意されています。 SignalR、Hubs、および永続的な接続の概要、または完全な SignalR アプリケーションを構築する方法を示すチュートリアルについては、「SignalR - はじめに」を参照してください。

概要

このドキュメントは、次のトピックに分かれています。

クライアントをプログラミングする方法のドキュメントについては、次のリソースを参照してください。

API リファレンス トピックへのリンクは、.NET 4.5 バージョンの API に関するページです。 .NET 4 を使用している場合は、 .NET 4 バージョンの API トピックを参照してください。

SignalR ルートを登録し、SignalR オプションを構成する方法

クライアントがハブへの接続に使用するルートを定義するには、アプリケーションの起動時に MapHubs メソッドを呼び出します。 MapHubsは、 クラスのSystem.Web.Routing.RouteCollection拡張メソッドです。 次の例は、 Global.asax ファイルで SignalR Hubs ルートを定義する方法を示しています。

using System.Web.Routing;
public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHubs();
    }
}

ASP.NET MVC アプリケーションに SignalR 機能を追加する場合は、SignalR ルートが他のルートの前に追加されていることを確認します。 詳細については、「チュートリアル: SignalR と MVC 4 を使用したはじめに」を参照してください。

The /signalr URL

既定では、クライアントがハブへの接続に使用するルート URL は "/signalr" です。 (この URL を、自動的に生成された JavaScript ファイル用の "/signalr/hubs" URL と混同しないでください。生成されたプロキシの詳細については、「 SignalR Hubs API ガイド - JavaScript クライアント - 生成されたプロキシとその機能」を参照してください。

このベース URL を SignalR で使用できない特別な状況が発生する可能性があります。たとえば、プロジェクトに signalr という名前のフォルダーがあり、名前を変更する必要はありません。 その場合は、次の例に示すようにベース URL を変更できます (サンプル コードの "/signalr" を目的の URL に置き換えます)。

URL を指定するサーバー コード

RouteTable.Routes.MapHubs("/signalr", new HubConfiguration());

URL を指定する JavaScript クライアント コード (生成されたプロキシを使用)

$.connection.hub.url = "/signalr"
$.connection.hub.start().done(init);

URL を指定する JavaScript クライアント コード (生成されたプロキシなし)

var connection = $.hubConnection("/signalr", { useDefaultPath: false });

URL を指定する .NET クライアント コード

var hubConnection = new HubConnection("http://contoso.com/signalr", useDefaultUrl: false);

SignalR オプションの構成

メソッドの MapHubs オーバーロードを使用すると、カスタム URL、カスタム依存関係リゾルバー、および次のオプションを指定できます。

  • ブラウザー クライアントからのクロスドメイン呼び出しを有効にします。

    通常、ブラウザーが から http://contoso.comページを読み込む場合、SignalR 接続は の同じドメインにあります http://contoso.com/signalr。 のページ http://contoso.com が への http://fabrikam.com/signalr接続を確立する場合、これはクロスドメイン接続です。 セキュリティ上の理由から、クロスドメイン接続は既定で無効になっています。 詳細については、「 ASP.NET SignalR Hubs API ガイド - JavaScript クライアント - クロスドメイン接続を確立する方法」を参照してください。

  • 詳細なエラー メッセージを有効にします。

    エラーが発生した場合、SignalR の既定の動作は、何が起こったかについての詳細を含まない通知メッセージをクライアントに送信することです。 悪意のあるユーザーがアプリケーションに対する攻撃で情報を使用できる可能性があるため、運用環境では詳細なエラー情報をクライアントに送信することはお勧めしません。 トラブルシューティングのために、このオプションを使用して、より有益なエラー報告を一時的に有効にすることができます。

  • 自動生成された JavaScript プロキシ ファイルを無効にします。

    既定では、ハブ クラスのプロキシを含む JavaScript ファイルは、URL "/signalr/hubs" に応答して生成されます。 JavaScript プロキシを使用しない場合、またはこのファイルを手動で生成してクライアント内の物理ファイルを参照する場合は、このオプションを使用してプロキシの生成を無効にすることができます。 詳細については、「 SignalR Hubs API ガイド - JavaScript クライアント - SignalR で生成されたプロキシの物理ファイルを作成する方法」を参照してください。

次の例は、 メソッドの呼び出しで SignalR 接続 URL とこれらのオプションを指定する方法を MapHubs 示しています。 カスタム URL を指定するには、例の "/signalr" を使用する URL に置き換えます。

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableCrossDomain = true;
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);

ハブ クラスを作成して使用する方法

ハブを作成するには、 Microsoft.Aspnet.Signalr.Hub から派生したクラスを作成します。 次の例は、チャット アプリケーションの単純なハブ クラスを示しています。

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

この例では、接続されているクライアントは メソッドを NewContosoChatMessage 呼び出すことができます。その場合、受信したデータは接続されているすべてのクライアントにブロードキャストされます。

ハブ オブジェクトの有効期間

Hub クラスをインスタンス化したり、サーバー上の独自のコードからそのメソッドを呼び出したりすることはありません。SignalR Hubs パイプラインによって行われるすべてのことが行われます。 SignalR は、クライアントがサーバーに接続、切断、またはメソッド呼び出しを行うときなど、ハブ操作を処理する必要があるたびに、Hub クラスの新しいインスタンスを作成します。

Hub クラスのインスタンスは一時的なものであるため、あるメソッド呼び出しから次のメソッド呼び出しまでの状態を維持するために使用することはできません。 サーバーがクライアントからメソッド呼び出しを受信するたびに、Hub クラスの新しいインスタンスによってメッセージが処理されます。 複数の接続とメソッド呼び出しによって状態を維持するには、データベース、Hub クラスの静的変数、または から Hub派生していない別のクラスなどの他のメソッドを使用します。 Hub クラスの静的変数などのメソッドを使用してメモリにデータを保持すると、アプリ ドメインがリサイクルされるとデータが失われます。

Hub クラスの外部で実行される独自のコードからクライアントにメッセージを送信する場合は、Hub クラス インスタンスをインスタンス化してメッセージを送信することはできませんが、Hub クラスの SignalR コンテキスト オブジェクトへの参照を取得することで実行できます。 詳細については、このトピックで後述 する「クライアント メソッドを呼び出し、ハブ クラスの外部からグループを管理する方法 」を参照してください。

JavaScript クライアントでのハブ名のキャメル ケース

既定では、JavaScript クライアントは、キャメル ケースバージョンのクラス名を使用して Hubs を参照します。 SignalR は、JavaScript コードが JavaScript 規則に準拠できるように、この変更を自動的に行います。 前の例は、JavaScript コードで と contosoChatHub 呼ばれます。

サーバー

public class ContosoChatHub : Hub

生成されたプロキシを使用する JavaScript クライアント

var contosoChatHubProxy = $.connection.contosoChatHub;

クライアントで使用する別の名前を指定する場合は、 属性を追加します HubName 。 属性を HubName 使用する場合、JavaScript クライアントではキャメル ケースに名前の変更はありません。

サーバー

[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub

生成されたプロキシを使用する JavaScript クライアント

var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;

複数のハブ

1 つのアプリケーションで複数のハブ クラスを定義できます。 これを行うと、接続は共有されますが、グループは別です。

  • すべてのクライアントは同じ URL を使用して、サービス ("/signalr" またはカスタム URL を指定した場合) との SignalR 接続を確立し、その接続はサービスによって定義されているすべてのハブに使用されます。

    1 つのクラスですべてのハブ機能を定義する場合と比較して、複数のハブのパフォーマンスに違いはありません。

  • すべてのハブが同じ HTTP 要求情報を取得します。

    すべてのハブは同じ接続を共有するため、サーバーが取得する HTTP 要求情報は、SignalR 接続を確立する元の HTTP 要求に含まれる情報のみです。 接続要求を使用してクエリ文字列を指定してクライアントからサーバーに情報を渡す場合、異なるハブに異なるクエリ文字列を指定することはできません。 すべてのハブは同じ情報を受け取ります。

  • 生成された JavaScript プロキシ ファイルには、1 つのファイル内のすべてのハブのプロキシが含まれます。

    JavaScript プロキシの詳細については、「 SignalR Hubs API ガイド - JavaScript クライアント - 生成されたプロキシとその機能」を参照してください。

  • グループはハブ内で定義されます。

    SignalR では、接続されているクライアントのサブセットにブロードキャストする名前付きグループを定義できます。 グループは、ハブごとに個別に管理されます。 たとえば、"Administrators" という名前のグループには、クラスの 1 つのクライアント ContosoChatHub セットが含まれており、同じグループ名がクラスの別のクライアント セットを StockTickerHub 参照します。

クライアントが呼び出すことができるハブ クラスでメソッドを定義する方法

クライアントから呼び出し可能にするメソッドをハブで公開するには、次の例に示すようにパブリック メソッドを宣言します。

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}
public class StockTickerHub : Hub
{
    public IEnumerable<Stock> GetAllStocks()
    {
        return _stockTicker.GetAllStocks();
    }
}

C# のメソッドの場合と同様に、複合型や配列など、戻り値の型とパラメーターを指定できます。 パラメーターで受け取ったり、呼び出し元に返したりするデータはすべて、JSON を使用してクライアントとサーバーの間で通信され、SignalR は複雑なオブジェクトとオブジェクトの配列のバインドを自動的に処理します。

JavaScript クライアントでのメソッド名のキャメルケース

既定では、JavaScript クライアントは、キャメル ケースバージョンのメソッド名を使用してハブ メソッドを参照します。 SignalR は、JavaScript コードが JavaScript 規則に準拠できるように、この変更を自動的に行います。

サーバー

public void NewContosoChatMessage(string userName, string message)

生成されたプロキシを使用する JavaScript クライアント

contosoChatHubProxy.server.newContosoChatMessage($(userName, message);

クライアントで使用する別の名前を指定する場合は、 属性を追加します HubMethodName

サーバー

[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)

生成されたプロキシを使用する JavaScript クライアント

contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

非同期的に実行するタイミング

メソッドが実行時間の長い場合、またはデータベース参照や Web サービス呼び出しなどの待機を伴う作業を行う必要がある場合は、戻り値の代わりに Task オブジェクトまたは Task<T> オブジェクト (戻り値の型のvoid代わりにT) を返して Hub メソッドを非同期にします。 メソッドからオブジェクトを Task 返すと、SignalR は が Task 完了するのを待ってから、ラップされていない結果をクライアントに送り返します。そのため、クライアントでメソッド呼び出しをコーディングする方法に違いはありません。

ハブ メソッドを非同期にすることで、WebSocket トランスポートを使用するときに接続がブロックされるのを回避できます。 ハブ メソッドが同期的に実行され、トランスポートが WebSocket である場合、同じクライアントからのハブ上のメソッドの後続の呼び出しは、Hub メソッドが完了するまでブロックされます。

次の例は、同期または非同期で実行するようにコーディングされたのと同じメソッドを示しています。その後に、いずれかのバージョンを呼び出すために機能する JavaScript クライアント コードが続きます。

Synchronous

public IEnumerable<Stock> GetAllStocks()
{
    // Returns data from memory.
    return _stockTicker.GetAllStocks();
}

非同期 - ASP.NET 4.5

public async Task<IEnumerable<Stock>> GetAllStocks()
{
    // Returns data from a web service.
    var uri = Util.getServiceUri("Stocks");
    using (HttpClient httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(uri);
        return (await response.Content.ReadAsAsync<IEnumerable<Stock>>());
    }
}

生成されたプロキシを使用する JavaScript クライアント

stockTickerHubProxy.server.getAllStocks().done(function (stocks) {
    $.each(stocks, function () {
        alert(this.Symbol + ' ' + this.Price);
    });
});

ASP.NET 4.5 で非同期メソッドを使用する方法の詳細については、「 ASP.NET MVC 4 での非同期メソッドの使用」を参照してください。

オーバーロードの定義

メソッドのオーバーロードを定義する場合は、各オーバーロード内のパラメーターの数が異なる必要があります。 異なるパラメーター型を指定するだけでオーバーロードを区別すると、Hub クラスはコンパイルされますが、クライアントがいずれかのオーバーロードを呼び出そうとすると、SignalR サービスは実行時に例外をスローします。

Hub クラスからクライアント メソッドを呼び出す方法

サーバーからクライアント メソッドを呼び出すには、Hub クラスの Clients メソッドで プロパティを使用します。 次の例は、接続されているすべてのクライアントで を呼び出す addNewMessageToPage サーバー コードと、JavaScript クライアントで メソッドを定義するクライアント コードを示しています。

サーバー

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

生成されたプロキシを使用する JavaScript クライアント

contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + htmlEncode(name)
        + '</strong>: ' + htmlEncode(message) + '<li>');
};

クライアント メソッドから戻り値を取得することはできません。などの int x = Clients.All.add(1,1) 構文は機能しません。

パラメーターには複合型と配列を指定できます。 次の例では、メソッド パラメーターで複合型をクライアントに渡します。

複雑なオブジェクトを使用してクライアント メソッドを呼び出すサーバー コード

public void SendMessage(string name, string message)
{
    Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}

複合オブジェクトを定義するサーバー コード

public class ContosoChatMessage
{
    public string UserName { get; set; }
    public string Message { get; set; }
}

生成されたプロキシを使用する JavaScript クライアント

var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
    console.log(message.UserName + ' ' + message.Message);
});

RPC を受信するクライアントの選択

Clients プロパティは、RPC を受け取るクライアントを指定するためのいくつかのオプションを提供する HubConnectionContext オブジェクトを返します。

  • 接続されたすべてのクライアント。

    Clients.All.addContosoChatMessageToPage(name, message);
    
  • 呼び出し元のクライアントのみ。

    Clients.Caller.addContosoChatMessageToPage(name, message);
    
  • 呼び出し元クライアントを除くすべてのクライアント。

    Clients.Others.addContosoChatMessageToPage(name, message);
    
  • 接続 ID で識別される特定のクライアント。

    Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
    

    この例では、呼び出し元のクライアントで を呼び出 addContosoChatMessageToPage し、 を使用 Clients.Callerする場合と同じ効果があります。

  • 接続 ID で識別される、指定されたクライアントを除くすべての接続済みクライアント。

    Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • 指定したグループ内のすべての接続済みクライアント。

    Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • 接続 ID で識別される、指定したクライアントを除く、指定されたグループ内のすべての接続済みクライアント。

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • 呼び出し元クライアントを除く、指定したグループ内のすべての接続済みクライアント。

    Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);
    

メソッド名のコンパイル時検証なし

指定したメソッド名は動的オブジェクトとして解釈されます。これは、IntelliSense またはコンパイル時の検証がないことを意味します。 式は実行時に評価されます。 メソッド呼び出しが実行されると、SignalR はメソッド名とパラメーター値をクライアントに送信し、クライアントに名前と一致するメソッドがある場合は、そのメソッドが呼び出され、パラメーター値が渡されます。 クライアントで一致するメソッドが見つからない場合、エラーは発生しません。 クライアント メソッドを呼び出すときに SignalR がクライアントにバックグラウンドで送信するデータの形式については、「 SignalR の概要」を参照してください。

大文字と小文字を区別しないメソッド名の一致

メソッド名の一致では、大文字と小文字は区別されません。 たとえば、Clients.All.addContosoChatMessageToPageサーバーでは、クライアントで 、addcontosochatmessagetopage、または addContosoChatMessageToPage が実行AddContosoChatMessageToPageされます。

非同期実行

呼び出すメソッドは非同期的に実行されます。 クライアントへのメソッド呼び出しの後に来るコードは、後続のコード行がメソッドの完了を待機するように指定しない限り、SignalR がクライアントへのデータの送信を完了するのを待たずに直ちに実行されます。 次のコード例では、2 つのクライアント メソッドを順番に実行する方法を示します。1 つは .NET 4.5 で動作するコードを使用し、もう 1 つは .NET 4 で動作するコードを使用しています。

.NET 4.5 の例

async public Task NewContosoChatMessage(string name, string message)
{
    await Clients.Others.addContosoChatMessageToPage(data);
    Clients.Caller.notifyMessageSent();
}

.NET 4 の例

public void NewContosoChatMessage(string name, string message)
{
    (Clients.Others.addContosoChatMessageToPage(data) as Task).ContinueWith(antecedent =>
        Clients.Caller.notifyMessageSent());
}

または ContinueWith を使用awaitして、次のコード行が実行される前にクライアント メソッドが終了するまで待機する場合、クライアントが実際にメッセージを受信してから次のコード行が実行されることを意味しません。 クライアント メソッド呼び出しの "完了" は、SignalR がメッセージの送信に必要なすべてのことを行ったことのみを意味します。 クライアントがメッセージを受信したことを確認する必要がある場合は、そのメカニズムを自分でプログラムする必要があります。 たとえば、ハブでメソッドを MessageReceived コーディングし、クライアントの メソッドで、クライアントで addContosoChatMessageToPage 必要な作業を行った後に を呼び出 MessageReceived すこともできます。 ハブでは MessageReceived 、元のメソッド呼び出しの実際のクライアント受信と処理に依存する作業を実行できます。

メソッド名として文字列変数を使用する方法

メソッド名として文字列変数を使用してクライアント メソッドを呼び出す場合は、Clients.AllInvoke(methodName, args...)IClientProxy呼び出します。Clients.OthersClients.Caller

public void NewContosoChatMessage(string name, string message)
{
    string methodToCall = "addContosoChatMessageToPage";
    IClientProxy proxy = Clients.All;
    proxy.Invoke(methodToCall, name, message);
}

Hub クラスからグループ メンバーシップを管理する方法

SignalR のグループは、接続されているクライアントの指定されたサブセットにメッセージをブロードキャストするためのメソッドを提供します。 グループには任意の数のクライアントを含めることができます。また、クライアントは任意の数のグループのメンバーにすることができます。

グループ メンバーシップを管理するには、Hub クラスの プロパティによってGroups提供される Add メソッドと Remove メソッドを使用します。 次の例は、 Groups.Add クライアント コードによって呼び出される Hub メソッドで使用される メソッドと Groups.Remove メソッド、その後にそれらを呼び出す JavaScript クライアント コードを示しています。

サーバー

public class ContosoChatHub : Hub
{
    public Task JoinGroup(string groupName)
    {
        return Groups.Add(Context.ConnectionId, groupName);
    }

    public Task LeaveGroup(string groupName)
    {
        return Groups.Remove(Context.ConnectionId, groupName);
    }
}

生成されたプロキシを使用する JavaScript クライアント

contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);

グループを明示的に作成する必要はありません。 実際には、 への呼び出しで初めて名前を指定すると、グループが自動的に Groups.Add作成され、その中のメンバーシップから最後の接続を削除すると削除されます。

グループ メンバーシップ リストまたはグループの一覧を取得するための API はありません。 SignalR は 、pub/sub モデルに基づいてクライアントとグループにメッセージを送信し、サーバーはグループまたはグループ メンバーシップのリストを保持しません。 これは、Web ファームにノードを追加するたびに SignalR が維持する状態を新しいノードに伝達する必要があるため、スケーラビリティを最大化するのに役立ちます。

Add メソッドと Remove メソッドの非同期実行

メソッドと Groups.Remove メソッドはGroups.Add非同期的に実行されます。 グループにクライアントを追加し、そのグループを使用してすぐにメッセージをクライアントに送信する場合は、メソッドが最初に完了していることを Groups.Add 確認する必要があります。 次のコード例は、.NET 4.5 で動作するコードを使用し、1 つは .NET 4 で動作するコードを使用して行う方法を示しています。

.NET 4.5 の例

async public Task JoinGroup(string groupName)
{
    await Groups.Add(Context.ConnectionId, groupName);
    Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
}

.NET 4 の例

public void JoinGroup(string groupName)
{
    (Groups.Add(Context.ConnectionId, groupName) as Task).ContinueWith(antecedent =>
        Clients.Group(groupName).addContosoChatMessageToPage(Context.ConnectionId + " added to group"));
}

グループ メンバーシップの永続化

SignalR はユーザーではなく接続を追跡するため、ユーザーが接続を確立するたびにユーザーを同じグループに含める場合は、ユーザーが新しい接続を確立するたびに を呼び出す Groups.Add 必要があります。

接続が一時的に失われた後、SignalR が接続を自動的に復元できる場合があります。 その場合、SignalR は同じ接続を復元し、新しい接続を確立しないため、クライアントのグループ メンバーシップが自動的に復元されます。 これは、一時的な中断がサーバーの再起動または障害の結果である場合でも可能です。これは、グループ メンバーシップを含む各クライアントの接続状態がクライアントにラウンドトリップされるためです。 サーバーがダウンし、接続がタイムアウトする前に新しいサーバーに置き換えられた場合、クライアントは新しいサーバーに自動的に再接続し、メンバーであるグループに再登録できます。

接続が失われた後、または接続がタイムアウトしたとき、またはクライアントが切断されたとき (ブラウザーが新しいページに移動したときなど) に、接続を自動的に復元できない場合、グループ メンバーシップは失われます。 次にユーザーが接続すると、新しい接続になります。 同じユーザーが新しい接続を確立したときにグループ メンバーシップを維持するには、アプリケーションでユーザーとグループ間の関連付けを追跡し、ユーザーが新しい接続を確立するたびにグループ メンバーシップを復元する必要があります。

接続と再接続の詳細については、このトピックで後述 する「Hub クラスで接続の有効期間イベントを処理する方法 」を参照してください。

単一ユーザー グループ

SignalR を使用するアプリケーションでは、通常、メッセージを送信したユーザーとメッセージを受信する必要があるユーザーを把握するために、ユーザーと接続の間の関連付けを追跡する必要があります。 グループは、これを行うための一般的に使用される 2 つのパターンのいずれかで使用されます。

  • 単一ユーザー グループ。

    ユーザー名をグループ名として指定し、ユーザーが接続または再接続するたびに現在の接続 ID をグループに追加できます。 グループに送信するユーザーにメッセージを送信するには。 この方法の欠点は、ユーザーがオンラインかオフラインかを調べる方法がグループに提供されない点です。

  • ユーザー名と接続 ID の間の関連付けを追跡します。

    各ユーザー名と 1 つ以上の接続 ID の間の関連付けをディクショナリまたはデータベースに格納し、ユーザーが接続または切断するたびに格納されているデータを更新できます。 ユーザーにメッセージを送信するには、接続 ID を指定します。 この方法の欠点は、より多くのメモリを必要とすることです。

Hub クラスで接続の有効期間イベントを処理する方法

接続の有効期間イベントを処理する一般的な理由は、ユーザーが接続されているかどうかを追跡し、ユーザー名と接続 ID の間の関連付けを追跡するためです。 クライアントが接続または切断したときに独自のコードを実行するには、次の OnConnected例に示すように、Hub クラスの 、 OnDisconnected、および OnReconnected 仮想メソッドをオーバーライドします。

public class ContosoChatHub : Hub
{
    public override Task OnConnected()
    {
        // Add your own code here.
        // For example: in a chat application, record the association between
        // the current connection ID and user name, and mark the user as online.
        // After the code in this method completes, the client is informed that
        // the connection is established; for example, in a JavaScript client,
        // the start().done callback is executed.
        return base.OnConnected();
    }

    public override Task OnDisconnected()
    {
        // Add your own code here.
        // For example: in a chat application, mark the user as offline, 
        // delete the association between the current connection id and user name.
        return base.OnDisconnected();
    }

    public override Task OnReconnected()
    {
        // Add your own code here.
        // For example: in a chat application, you might have marked the
        // user as offline after a period of inactivity; in that case 
        // mark the user as online again.
        return base.OnReconnected();
    }
}

OnConnected、OnDisconnected、OnReconnected が呼び出されると

ブラウザーが新しいページに移動するたびに、新しい接続を確立する必要があります。つまり、SignalR はメソッドの後に メソッドをOnDisconnectedOnConnected実行します。 SignalR では、新しい接続が確立されると、常に新しい接続 ID が作成されます。

この OnReconnected メソッドは、接続がタイムアウトする前にケーブルが一時的に切断されて再接続された場合など、SignalR が自動的に復旧できる接続が一時的に中断された場合に呼び出されます。メソッドは OnDisconnected 、クライアントが切断され、SignalR が自動的に再接続できない場合 (ブラウザーが新しいページに移動したときなど) に呼び出されます。 したがって、特定のクライアントで発生する可能性のあるイベントのシーケンスはOnConnected、、、OnReconnectedOnDisconnected、または OnConnectedですOnDisconnected。 特定の接続のシーケンス OnConnected、、 OnDisconnectedOnReconnected は表示されません。

サーバーが OnDisconnected ダウンしたときやアプリ ドメインがリサイクルされる場合など、一部のシナリオでは メソッドが呼び出されません。 別のサーバーがオンラインになったり、アプリ ドメインのリサイクルが完了したりすると、一部のクライアントが再接続してイベントを OnReconnected 発生できる場合があります。

詳細については、「 SignalR の接続有効期間イベントの概要と処理」を参照してください。

呼び出し元の状態が設定されていません

接続有効期間イベント ハンドラー メソッドはサーバーから呼び出されます。つまり、クライアント上の オブジェクトに state 配置した状態は、サーバーの プロパティに Caller 設定されません。 オブジェクトと プロパティの詳細stateについては、このトピックで後述する「クライアントとハブ クラスの間で状態を渡す方法」を参照してください。Caller

Context プロパティからクライアントに関する情報を取得する方法

クライアントに関する情報を取得するには、Hub クラスの プロパティを使用 Context します。 プロパティは Context 、次の情報へのアクセスを提供する HubCallerContext オブジェクトを返します。

  • 呼び出し元クライアントの接続 ID。

    string connectionID = Context.ConnectionId;
    

    接続 ID は、SignalR によって割り当てられる GUID です (独自のコードで値を指定することはできません)。 接続ごとに 1 つの接続 ID があり、アプリケーションに複数のハブがある場合は、すべてのハブで同じ接続 ID が使用されます。

  • HTTP ヘッダー データ。

    System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
    

    から Context.HeadersHTTP ヘッダーを取得することもできます。 同じものへの複数の参照の理由は、 Context.Headers 最初に作成され、後で プロパティが Context.Request 追加され Context.Headers 、下位互換性のために保持されたためです。

  • 文字列データのクエリを実行します。

    System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString;
    string parameterValue = queryString["parametername"]
    

    からクエリ文字列データ Context.QueryStringを取得することもできます。

    このプロパティで取得するクエリ文字列は、SignalR 接続を確立した HTTP 要求で使用された文字列です。 クライアントにクエリ文字列パラメーターを追加するには、接続を構成します。これは、クライアントに関するデータをクライアントからサーバーに渡す便利な方法です。 次の例は、生成されたプロキシを使用するときに JavaScript クライアントにクエリ文字列を追加する方法の 1 つを示しています。

    $.connection.hub.qs = { "version" : "1.0" };
    

    クエリ文字列パラメーターの設定の詳細については、 JavaScript および .NET クライアントの API ガイドを参照してください。

    接続に使用されるトランスポート メソッドは、SignalR によって内部的に使用されるその他の値と共に、クエリ文字列データで見つけることができます。

    string transportMethod = queryString["transport"];
    

    transportMethod 値は、"webSockets"、"serverSentEvents"、"foreverFrame"、または "longPolling" になります。 イベント ハンドラー メソッドでOnConnectedこの値をチェックした場合、一部のシナリオでは、接続の最終的なネゴシエートされたトランスポート メソッドではないトランスポート値を最初に取得する場合があることに注意してください。 その場合、メソッドは例外をスローし、最終的なトランスポート メソッドが確立されると後で再度呼び出されます。

  • クッキー。

    System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
    

    から Cookie を Context.RequestCookies取得することもできます。

  • ユーザー情報。

    System.Security.Principal.IPrincipal user = Context.User;
    
  • 要求の HttpContext オブジェクト。

    System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();
    

    SignalR 接続の HttpContext.Current オブジェクトを取得する代わりに、 HttpContext このメソッドを使用します。

クライアントとハブ クラスの間で状態を渡す方法

クライアント プロキシは、 state 各メソッド呼び出しでサーバーに送信するデータを格納できるオブジェクトを提供します。 サーバーでは、クライアントによって呼び出される Clients.Caller Hub メソッドの プロパティでこのデータにアクセスできます。 プロパティは Clients.Caller 、接続の有効期間イベント ハンドラー メソッド OnConnected、、 OnDisconnectedおよび OnReconnectedには設定されません。

オブジェクトと プロパティのデータの state 作成または更新は Clients.Caller 、両方向で機能します。 サーバー内の値を更新すると、クライアントに渡されます。

次の例は、すべてのメソッド呼び出しでサーバーに送信するための状態を格納する JavaScript クライアント コードを示しています。

contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";

次の例は、.NET クライアントの同等のコードを示しています。

contosoChatHubProxy["userName"] = "Fadi Fakhouri";
chatHubProxy["computerName"] = "fadivm1";

Hub クラスでは、 プロパティ内のこのデータに Clients.Caller アクセスできます。 次の例は、前の例で参照されている状態を取得するコードを示しています。

public void NewContosoChatMessage(string data)
{
    string userName = Clients.Caller.userName;
    string computerName = Clients.Caller.computerName;
    Clients.Others.addContosoChatMessageToPage(message, userName, computerName);
}

Note

状態を永続化するためのこのメカニズムは、または Clients.Caller プロパティに配置するすべてのものがすべてのメソッド呼び出しでstateラウンドトリップされるため、大量のデータを対象としたものではありません。 これは、ユーザー名やカウンターなどの小さな項目に役立ちます。

Hub クラスでエラーを処理する方法

Hub クラス メソッドで発生するエラーを処理するには、次のメソッドのいずれかまたは両方を使用します。

  • メソッド コードを try-catch ブロックでラップし、例外オブジェクトをログに記録します。 デバッグ目的では、例外をクライアントに送信できますが、セキュリティ上の理由から運用環境のクライアントに詳細情報を送信することはお勧めしません。

  • OnIncomingError メソッドを処理する Hubs パイプライン モジュールを作成します。 次の例は、エラーをログに記録するパイプライン モジュールの後に、モジュールを Hubs パイプラインに挿入する Global.asax のコードを示しています。

    public class ErrorHandlingPipelineModule : HubPipelineModule
    {
        protected override void OnIncomingError(Exception ex, IHubIncomingInvokerContext context)
        {
            Debug.WriteLine("=> Exception " + ex.Message);
            if (ex.InnerException != null)
            {
                Debug.WriteLine("=> Inner Exception " + ex.InnerException.Message);
            }
            base.OnIncomingError(ex, context);
        }
    }
    
    protected void Application_Start(object sender, EventArgs e)
    {
        GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); 
        RouteTable.Routes.MapHubs();
    }
    

ハブ パイプライン モジュールの詳細については、このトピックで後述 する「Hubs パイプラインをカスタマイズする方法 」を参照してください。

トレースを有効にする方法

サーバー側トレースを有効にするには、システムを追加します。次の例に示すように、Web.config ファイルに要素を診断します。

<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit https://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="SignalRSamples" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;" />
    <add name="SignalRSamplesWithMARS" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;MultipleActiveResultSets=true;" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
  </system.webServer>
  <system.diagnostics>
    <sources>
      <source name="SignalR.SqlMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
     <source name="SignalR.ServiceBusMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
     </source>
     <source name="SignalR.ScaleoutMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.Transports.WebSocketTransport">
        <listeners>
          <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.ServerSentEventsTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.ForeverFrameTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.LongPollingTransport">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.TransportHeartBeat">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="SignalRSwitch" value="Verbose" />
    </switches>
    <sharedListeners>
      <add name="SignalR-Transports" 
           type="System.Diagnostics.TextWriterTraceListener" 
           initializeData="transports.log.txt" />
        <add name="SignalR-Bus"
           type="System.Diagnostics.TextWriterTraceListener"
           initializeData="bus.log.txt" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
  </entityFramework>
</configuration>

Visual Studio でアプリケーションを実行すると、[ 出力 ] ウィンドウでログを表示できます。

クライアント メソッドを呼び出し、ハブ クラスの外部からグループを管理する方法

ハブ クラスとは異なるクラスからクライアント メソッドを呼び出すには、ハブの SignalR コンテキスト オブジェクトへの参照を取得し、これを使用してクライアント上のメソッドを呼び出したり、グループを管理したりします。

次のサンプル StockTicker クラスは、コンテキスト オブジェクトを取得し、 クラスのインスタンスに格納し、クラス インスタンスを静的プロパティに格納し、シングルトン クラス インスタンスのコンテキストを使用して、 という名前StockTickerHubのハブに接続されているクライアントで メソッドを呼び出updateStockPriceします。

// For the complete example, go to 
// http://www.asp.net/signalr/overview/signalr-1x/getting-started/tutorial-server-broadcast-with-aspnet-signalr
// This sample only shows code related to getting and using the SignalR context.
public class StockTicker
{
    // Singleton instance
    private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
        () => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>()));

    private IHubContext _context;

    private StockTicker(IHubContext context)
    {
        _context = context;
    }

    // This method is invoked by a Timer object.
    private void UpdateStockPrices(object state)
    {
        foreach (var stock in _stocks.Values)
        {
            if (TryUpdateStockPrice(stock))
            {
                _context.Clients.All.updateStockPrice(stock);
            }
        }
    }

有効期間の長いオブジェクトでコンテキストを複数回使用する必要がある場合は、参照を 1 回取得し、毎回再度取得するのではなく保存します。 コンテキストを 1 回取得すると、SignalR は、Hub メソッドがクライアント メソッドを呼び出すのと同じ順序でクライアントにメッセージを送信します。 ハブに SignalR コンテキストを使用する方法を示すチュートリアルについては、「 ASP.NET SignalR を使用したサーバー ブロードキャスト」を参照してください。

クライアント メソッドの呼び出し

RPC を受け取るクライアントを指定できますが、ハブ クラスからを呼び出す場合よりもオプションが少なくなります。 その理由は、コンテキストがクライアントからの特定の呼び出しに関連付けられていないため、現在の接続 ID (、、 Clients.CallerClients.OthersInGroupなどClients.Others) の知識を必要とするメソッドは使用できないためです。 次のオプションを使用できます。

  • 接続されたすべてのクライアント。

    context.Clients.All.addContosoChatMessageToPage(name, message);
    
  • 接続 ID で識別される特定のクライアント。

    context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);
    
  • 接続 ID で識別される、指定されたクライアントを除くすべての接続済みクライアント。

    context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • 指定したグループ内のすべての接続済みクライアント。

    context.Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • 接続 ID で識別される、指定したクライアントを除く、指定されたグループ内のすべての接続済みクライアント。

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    

Hub クラスのメソッドから非 Hub クラスを呼び出す場合は、現在の接続 ID を渡し、その ID を 、、または Clients.Group と共Clients.Clientに使用して、、Clients.Others、または Clients.OthersInGroupをシミュレートClients.Callerできます。 Clients.AllExcept 次の例では、 クラスは MoveShapeHub クラスに Broadcaster 接続 ID を渡して、 クラスを Broadcaster シミュレート Clients.Othersできるようにします。

// For the complete example, see
// http://www.asp.net/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr
// This sample only shows code that passes connection ID to the non-Hub class,
// in order to simulate Clients.Others.
public class MoveShapeHub : Hub
{
    // Code not shown puts a singleton instance of Broadcaster in this variable.
    private Broadcaster _broadcaster;

    public void UpdateModel(ShapeModel clientModel)
    {
        clientModel.LastUpdatedBy = Context.ConnectionId;
        // Update the shape model within our broadcaster
        _broadcaster.UpdateShape(clientModel);
    }
}

public class Broadcaster
{
    public Broadcaster()
    {
        _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
    }

    public void UpdateShape(ShapeModel clientModel)
    {
        _model = clientModel;
        _modelUpdated = true;
    }

    // Called by a Timer object.
    public void BroadcastShape(object state)
    {
        if (_modelUpdated)
        {
            _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
            _modelUpdated = false;
        }
    }
}

グループ メンバーシップの管理

グループを管理する場合は、Hub クラスと同じオプションがあります。

  • クライアントをグループに追加する

    context.Groups.Add(connectionID, groupName);
    
  • クライアントをグループから削除する

    context.Groups.Remove(connectionID, groupName);
    

Hubs パイプラインをカスタマイズする方法

SignalR を使用すると、独自のコードをハブ パイプラインに挿入できます。 次の例は、クライアントから受信した各着信メソッド呼び出しと、クライアントで呼び出された発信メソッド呼び出しをログに記録するカスタム ハブ パイプライン モジュールを示しています。

public class LoggingPipelineModule : HubPipelineModule 
{ 
    protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context) 
    { 
        Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name); 
        return base.OnBeforeIncoming(context); 
    }   
    protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context) 
    { 
        Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub); 
        return base.OnBeforeOutgoing(context); 
    } 
}

Global.asax ファイルの次のコードは、ハブ パイプラインで実行するモジュールを登録します。

protected void Application_Start(object sender, EventArgs e) 
{ 
    GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule()); 
    RouteTable.Routes.MapHubs();
}

オーバーライドできるさまざまなメソッドがあります。 完全な一覧については、「 HubPipelineModule メソッド」を参照してください。