ASP.NET Core での Websocket のサポートWebSockets support in ASP.NET Core

作成者: Tom Dykstra および Andrew Stanton-NurseBy Tom Dykstra and Andrew Stanton-Nurse

この記事では、ASP.NET Core で Websocket の使用を開始する方法について説明します。This article explains how to get started with WebSockets in ASP.NET Core. WebSocket (RFC 6455) は、TCP 接続を使用した双方向の永続的通信チャネルを有効にするプロトコルです。WebSocket (RFC 6455) is a protocol that enables two-way persistent communication channels over TCP connections. このプロトコルは、チャット、ダッシュボード、ゲーム アプリなど、高速かつリアルタイムのコミュニケーションを活用するアプリで使用されます。It's used in apps that benefit from fast, real-time communication, such as chat, dashboard, and game apps.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download). 実行方法How to run.

SignalR

ASP.NET Core SignalR は、アプリへのリアルタイム Web 機能の追加を簡単にするライブラリです。ASP.NET Core SignalR is a library that simplifies adding real-time web functionality to apps. 可能なかぎり、WebSocket が使用されます。It uses WebSockets whenever possible.

ほとんどのアプリケーションでは、生の WebSocket よりも SignalR が推奨されます。For most applications, we recommend SignalR over raw WebSockets. SignalR には、WebSocket を使用できない環境の場合にトランスポートのフォールバックが用意されています。 provides transport fallback for environments where WebSockets is not available. シンプルなリモート プロシージャ呼び出しアプリ モデルも用意されています。It also provides a simple remote procedure call app model. また、ほとんどのシナリオで、SignalR には生の WebSocket を使用した場合と比較してパフォーマンス上の大きなデメリットがありません。And in most scenarios, SignalR has no significant performance disadvantage compared to using raw WebSockets.

必須コンポーネントPrerequisites

  • ASP.NET Core 1.1 以降ASP.NET Core 1.1 or later

  • ASP.NET Core をサポートする任意の OS:Any OS that supports ASP.NET Core:

    • Windows 7 / Windows Server 2008 以降Windows 7 / Windows Server 2008 or later
    • LinuxLinux
    • macOSmacOS
  • アプリが IIS を含む Windows 上で実行されている場合:If the app runs on Windows with IIS:

    • Windows 8 / Windows Server 2012 以降Windows 8 / Windows Server 2012 or later
    • IIS 8 / IIS 8 ExpressIIS 8 / IIS 8 Express
    • WebSockets を有効にする必要があります (「IIS/IIS Express のサポート」セクションを参照してください)。WebSockets must be enabled (See the IIS/IIS Express support section.).
  • アプリが HTTP.sys で実行されている場合:If the app runs on HTTP.sys:

    • Windows 8 / Windows Server 2012 以降Windows 8 / Windows Server 2012 or later
  • サポートされているブラウザーについては、 https://caniuse.com/#feat=websockets を参照してください。For supported browsers, see https://caniuse.com/#feat=websockets.

NuGet パッケージNuGet package

Microsoft.AspNetCore.WebSockets パッケージをインストールします。Install the Microsoft.AspNetCore.WebSockets package.

ミドルウェアの構成Configure the middleware

Startup クラスの Configure メソッドに、Websocket ミドルウェアを追加します。Add the WebSockets middleware in the Configure method of the Startup class:

app.UseWebSockets();

次の設定を構成できます。The following settings can be configured:

  • KeepAliveInterval: プロキシの接続の維持を保証する、クライアントに "ping" フレームを送信する頻度。KeepAliveInterval - How frequently to send "ping" frames to the client to ensure proxies keep the connection open. 既定値は 2 分です。The default is two minutes.
  • ReceiveBufferSize: データの受信に使用されるバッファーのサイズ。ReceiveBufferSize - The size of the buffer used to receive data. これは、上級ユーザーが、データのサイズに応じたパフォーマンス調整のために変更する必要がある場合があります。Advanced users may need to change this for performance tuning based on the size of the data. 既定値は 4 KB です。The default is 4 KB.

次の設定を構成できます。The following settings can be configured:

  • KeepAliveInterval: プロキシの接続の維持を保証する、クライアントに "ping" フレームを送信する頻度。KeepAliveInterval - How frequently to send "ping" frames to the client to ensure proxies keep the connection open. 既定値は 2 分です。The default is two minutes.
  • ReceiveBufferSize: データの受信に使用されるバッファーのサイズ。ReceiveBufferSize - The size of the buffer used to receive data. これは、上級ユーザーが、データのサイズに応じたパフォーマンス調整のために変更する必要がある場合があります。Advanced users may need to change this for performance tuning based on the size of the data. 既定値は 4 KB です。The default is 4 KB.
  • AllowedOrigins - WebSocket 要求で許可される配信元ヘッダー値の一覧。AllowedOrigins - A list of allowed Origin header values for WebSocket requests. 既定では、すべての配信元が許可されています。By default, all origins are allowed. 詳細については、下記の "WebSocket の配信元の制限" を参照してください。See "WebSocket origin restriction" below for details.
var webSocketOptions = new WebSocketOptions() 
{
    KeepAliveInterval = TimeSpan.FromSeconds(120),
    ReceiveBufferSize = 4 * 1024
};

app.UseWebSockets(webSocketOptions);

WebSocket の要求の受け入れAccept WebSocket requests

以降の要求ライフサイクルのどこかで (たとえば、以降の Configure メソッドまたはアクション メソッド)、それが WebSocket 要求であるかを確認し、WebSocket 要求を受け入れます。Somewhere later in the request life cycle (later in the Configure method or in an action method, for example) check if it's a WebSocket request and accept the WebSocket request.

次の例は、以降の Configure メソッドから抜粋したものです。The following example is from later in the Configure method:

app.Use(async (context, next) =>
{
    if (context.Request.Path == "/ws")
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
            await Echo(context, webSocket);
        }
        else
        {
            context.Response.StatusCode = 400;
        }
    }
    else
    {
        await next();
    }

});

WebSocket 要求はどの URL からも受け取る場合がありますが、このサンプル コードでは /ws の要求のみを受け取ります。A WebSocket request could come in on any URL, but this sample code only accepts requests for /ws.

WebSocket を使用するとき、接続中、ミドルウェア パイプラインの実行を維持する必要がありますWhen using a WebSocket, you must keep the middleware pipeline running for the duration of the connection. ミドルウェア パイプラインの修了後に WebSocket メッセージを送信するか、受信する場合、次のような例外を受け取ることがあります。If you attempt to send or receive a WebSocket message after the middleware pipeline ends, you may get an exception like the following:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.

バックグラウンド サービスを利用してデータを WebSocket に書き込む場合、ミドルウェア パイプラインの実行を維持します。If you're using a background service to write data to a WebSocket, make sure you keep the middleware pipeline running. これは TaskCompletionSource<TResult> を使用して行います。Do this by using a TaskCompletionSource<TResult>. TaskCompletionSource をバックグラウンド サービスに渡し、WebSocket が終わったとき、それに TrySetResult を呼び出させます。Pass the TaskCompletionSource to your background service and have it call TrySetResult when you finish with the WebSocket. 次の例に示すように、要求中に Task プロパティの await を行います。Then await the Task property during the request, as shown in the following example:

app.Use(async (context, next) => {
    var socket = await context.WebSockets.AcceptWebSocketAsync();
    var socketFinishedTcs = new TaskCompletionSource<object>();

    BackgroundSocketProcessor.AddSocket(socket, socketFinishedTcs); 

    await socketFinishedTcs.Task;
});

WebSocket の終了例外は、アクション メソッドから早く戻りすぎた場合にも発生する可能性があります。The WebSocket closed exception can also happen if you return too soon from an action method. アクション メソッドでソケットを受け入れる場合は、そのソケットを使用するコードが完了するまで待ち、アクション メソッドから戻ってください。If you accept a socket in an action method, wait for the code that uses the socket to complete before returning from the action method.

重大なスレッドの問題を引き起こす可能性があるので、ソケットの完了を待つために Task.Wait()Task.Result、または同様のブロック呼び出しを使用しないでください。Never use Task.Wait(), Task.Result, or similar blocking calls to wait for the socket to complete, as that can cause serious threading issues. 常に await を使用します。Always use await.

メッセージの送受信Send and receive messages

AcceptWebSocketAsync メソッドは、TCP 接続を WebSocket 接続にアップグレードし、WebSocket オブジェクトを提供します。The AcceptWebSocketAsync method upgrades the TCP connection to a WebSocket connection and provides a WebSocket object. メッセージの送受信に、WebSocket オブジェクトを使用します。Use the WebSocket object to send and receive messages.

前に示した、WebSocket 要求を受け入れるコードが、WebSocket オブジェクトを Echo メソッドに渡します。The code shown earlier that accepts the WebSocket request passes the WebSocket object to an Echo method. このコードは、メッセージを受信し、同じメッセージをすぐに送信します。The code receives a message and immediately sends back the same message. クライアントが接続を閉じるまで、メッセージがループで送受信されます。Messages are sent and received in a loop until the client closes the connection:

private async Task Echo(HttpContext context, WebSocket webSocket)
{
    var buffer = new byte[1024 * 4];
    WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
    while (!result.CloseStatus.HasValue)
    {
        await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);

        result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
    }
    await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}

このループを開始する前に、WebSocket 接続を受け入れた場合、ミドルウェア パイプラインは終了します。When accepting the WebSocket connection before beginning the loop, the middleware pipeline ends. ソケットを閉じると、パイプラインはアンワインドされます。Upon closing the socket, the pipeline unwinds. つまり、WebSocket を受け入れると、要求はパイプラインでの先への移動を中止します。That is, the request stops moving forward in the pipeline when the WebSocket is accepted. ループを終了し、ソケットを閉じた場合、要求はパイプラインのバックアップを続けます。When the loop is finished and the socket is closed, the request proceeds back up the pipeline.

クライアントの切断の処理Handle client disconnects

接続の損失によってクライアントが切断されても、サーバーに自動的に通知されるわけではありません。The server is not automatically informed when the client disconnects due to loss of connectivity. サーバーが切断メッセージを受信するのは、クライアントがそれを送信した場合のみです。インターネット接続が失われた場合、これを実行することはできません。The server receives a disconnect message only if the client sends it, which can't be done if the internet connection is lost. これが発生した場合に何らかのアクションを実行したい場合は、特定の時間枠内でクライアントからの受信を待つタイムアウトを設定します。If you want to take some action when that happens, set a timeout after nothing is received from the client within a certain time window.

クライアントが常にメッセージを送信するとは限らず、その接続がアイドル状態になっただけでタイムアウトしたくない場合は、X 秒ごとに ping メッセージを送信するタイマーをクライアントに使用させます。If the client isn't always sending messages and you don't want to timeout just because the connection goes idle, have the client use a timer to send a ping message every X seconds. サーバー上では、前のものから 2*X 秒以内にメッセージが到着しなかった場合に、接続を終了してクライアントが切断されたことをレポートします。On the server, if a message hasn't arrived within 2*X seconds after the previous one, terminate the connection and report that the client disconnected. 予想される 2 倍の期間を待機することで、ping メッセージを遅らせる可能性のあるネットワークの遅延のために余分な時間を残します。Wait for twice the expected time interval to leave extra time for network delays that might hold up the ping message.

WebSocket の配信元の制限WebSocket origin restriction

CORS で提供される保護は、WebSocket には適用されません。The protections provided by CORS don't apply to WebSockets. ブラウザーでは以下を実行しませんBrowsers do not:

  • CORS の事前要求を実行する。Perform CORS pre-flight requests.
  • WebSocket 要求を行うときに Access-Control ヘッダーに指定された制限を考慮する。Respect the restrictions specified in Access-Control headers when making WebSocket requests.

ただし、WebSocket 要求を発行するときにはブラウザーから Origin ヘッダーが送信されます。However, browsers do send the Origin header when issuing WebSocket requests. 予期した配信元からの WebSocket のみが許可されるように、アプリケーションでこれらのヘッダーが検証されるように構成する必要があります。Applications should be configured to validate these headers to ensure that only WebSockets coming from the expected origins are allowed.

"https://server.com" でサーバーを、"https://client.com" でクライアントをホスティングしている場合は、検証のために "https://client.com" を WebSocket の AllowedOrigins 一覧に追加します。If you're hosting your server on "https://server.com" and hosting your client on "https://client.com", add "https://client.com" to the AllowedOrigins list for WebSockets to verify.

var webSocketOptions = new WebSocketOptions()
{
    KeepAliveInterval = TimeSpan.FromSeconds(120),
    ReceiveBufferSize = 4 * 1024
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");

app.UseWebSockets(webSocketOptions);

注意

Origin ヘッダーは、クライアントによって制御され、Referer のように偽装することができます。The Origin header is controlled by the client and, like the Referer header, can be faked. これらのヘッダーを認証メカニズムとして使用しないでくださいDo not use these headers as an authentication mechanism.

IIS/IIS Express のサポートIIS/IIS Express support

IIS/IIS Express 8 以降を含む、Windows Server 2012 以降および Windows 8 以降では、WebSocket プロトコルをサポートします。Windows Server 2012 or later and Windows 8 or later with IIS/IIS Express 8 or later has support for the WebSocket protocol.

注意

IIS Express を使用する場合、WebSockets は常に有効になります。WebSockets are always enabled when using IIS Express.

IIS での WebSockets の有効化Enabling WebSockets on IIS

Windows Server 2012 以降で WebSocket プロトコルのサポートを有効にするにはTo enable support for the WebSocket protocol on Windows Server 2012 or later:

注意

これらの手順は、IIS Express を使用する場合は必要ありませんThese steps are not required when using IIS Express

  1. [管理] メニューから役割と機能の追加ウィザードを使用するか、サーバー マネージャーにあるリンクを使用します。Use the Add Roles and Features wizard from the Manage menu or the link in Server Manager.
  2. [役割ベースまたは機能ベースのインストール] を選択します。Select Role-based or Feature-based Installation. [次へ] を選択します。Select Next.
  3. 適切なサーバーを選択します (既定では、ローカル サーバーが選択されます)。Select the appropriate server (the local server is selected by default). [次へ] を選択します。Select Next.
  4. [役割] ツリーで [Web サーバー (IIS)] を展開し、 [Web サーバー][アプリケーション開発] の順に展開します。Expand Web Server (IIS) in the Roles tree, expand Web Server, and then expand Application Development.
  5. [WebSocket プロトコル] を選択します。Select WebSocket Protocol. [次へ] を選択します。Select Next.
  6. 追加機能が不要な場合は、 [次へ] を選択します。If additional features aren't needed, select Next.
  7. [インストール] を選択します。Select Install.
  8. インストールが完了したら、 [閉じる] を選択してウィザードを終了します。When the installation completes, select Close to exit the wizard.

Windows 8 以降で WebSocket プロトコルのサポートを有効にするにはTo enable support for the WebSocket protocol on Windows 8 or later:

注意

これらの手順は、IIS Express を使用する場合は必要ありませんThese steps are not required when using IIS Express

  1. [コントロール パネル] > [プログラム] > [プログラムと機能] > [Windows の機能の有効化または無効化] (画面の左側) に移動します。Navigate to Control Panel > Programs > Programs and Features > Turn Windows features on or off (left side of the screen).
  2. 次のノード: [インターネット インフォメーション サービス] > [World Wide Web サービス] > [アプリケーション開発機能] を開きます。Open the following nodes: Internet Information Services > World Wide Web Services > Application Development Features.
  3. [WebSocket プロトコル] 機能を選択します。Select the WebSocket Protocol feature. [OK] を選択します。Select OK.

Node.js で socket.io を使用する場合に WebSocket を無効にするDisable WebSocket when using socket.io on Node.js

Node.jssocket.io で WebSocket サポートを使用する場合、webSocket 要素を使用する既定の WebSocket モジュールを web.config または applicationHost.config で無効にします。この手順が実行されない場合、IIS WebSocket モジュールは Node.js とアプリ以外の WebSocket コミュニケーションを処理しようとします。If using the WebSocket support in socket.io on Node.js, disable the default IIS WebSocket module using the webSocket element in web.config or applicationHost.config. If this step isn't performed, the IIS WebSocket module attempts to handle the WebSocket communication rather than Node.js and the app.

<system.webServer>
  <webSocket enabled="false" />
</system.webServer>

サンプル アプリSample app

この記事に添えられているサンプル アプリは、エコー アプリです。The sample app that accompanies this article is an echo app. これには、WebSocket 接続を作成する Web ページがあり、サーバーが受け取るすべてのメッセージをクライアントに再送信します。It has a web page that makes WebSocket connections, and the server resends any messages it receives back to the client. コマンド プロンプトからアプリを実行し (IIS Express を使用した Visual Studio からは実行するように設定されていません)、 http://localhost:5000 に移動します。Run the app from a command prompt (it's not set up to run from Visual Studio with IIS Express) and navigate to http://localhost:5000. Web ページの左上に、接続の状態が示されます。The web page shows the connection status in the upper left:

Web ページの初期状態

[接続] を選択し、表示されている URL に WebSocket 要求を送信します。Select Connect to send a WebSocket request to the URL shown. テスト メッセージを入力し、 [送信] を選択します。Enter a test message and select Send. 完了したら、 [Close Socket](ソケットを閉じる) を選択します。When done, select Close Socket. [Communication Log](コミュニケーション ログ) セクションに、発生した各オープン、送信、クローズのアクションが表示されます。The Communication Log section reports each open, send, and close action as it happens.

Web ページの初期状態