バックグラウンドでのネットワーク通信Network communications in the background

フォアグラウンドでないときにネットワーク通信を続けるため、アプリはバックグラウンド タスクと次のいずれかのオプションを使うことができます。To continue network communication while it's not in the foreground, your app can use background tasks and one of these two options.

  • ソケット ブローカー:Socket broker. 長期的な接続にソケットを使うアプリは、フォアグラウンドから離れるときにソケットの所有権をシステムのソケット ブローカーに委任できます。If your app uses sockets for long-term connections then, when it leaves the foreground, it can delegate ownership of a socket to a system socket broker. ブローカーは、トラフィックがソケットに到着したときにアプリをアクティブ化して所有権をアプリに戻し、その後にアプリが着信トラフィックを処理します。The broker then: activates your app when traffic arrives on the socket; transfers ownership back to your app; and your app then processes the arriving traffic.
  • コントロール チャネル トリガー。Control channel triggers.

バックグラウンド タスクでのネットワーク操作の実行Performing network operations in background tasks

  • パケットを受信し、有効期間が短いタスクを実行する必要がある場合は、SocketActivityTrigger を使用して、バックグラウンド タスクをアクティブにします。Use a SocketActivityTrigger to activate the background task when a packet is received and you need to perform a short-lived task. タスクを実行すると、電力を節約するためにバックグラウンド タスクが終了します。After performing the task, the background task should terminate in order to save power.
  • パケットを受信し、有効期間が長いタスクを実行する必要がある場合は、ControlChannelTrigger を使用して、バックグラウンド タスクをアクティブにします。Use a ControlChannelTrigger to activate the background task when a packet is received and you need to perform a long-lived task.

ネットワーク関連の条件とフラグNetwork-related conditions and flags

  • バックグラウンド タスク BackgroundTaskBuilder.AddConditionInternetAvailable 条件を追加して、ネットワーク スタックが実行されるまで、バックグラウンド タスクのトリガーを遅らせます。Add the InternetAvailable condition to your background task BackgroundTaskBuilder.AddCondition to delay triggering the background task until the network stack is running. この条件では、ネットワークが起動するまでバックグラウンド タスクが実行されないため、電力が節約されます。This condition saves power because the background task won't execute until the network is up. この条件では、リアルタイムのアクティブ化は行われません。This condition does not provide real-time activation.

使用するトリガーに関係なく、バックグラウンド タスクで IsNetworkRequested を設定すると、バックグラウンド タスクが実行されている間、ネットワークは稼働状態のままになります。Regardless of the trigger you use, set IsNetworkRequested on your background task to ensure that the network stays up while the background task runs. これによって、デバイスがコネクト スタンバイ モードに入っている場合でも、タスクの実行中はネットワークを稼働状態に保つようにバックグラウンド タスク インフラストラクチャに指示されます。This tells the background task infrastructure to keep the network up while the task is executing, even if the device has entered Connected Standby mode. バックグラウンド タスクが IsNetworkRequested を使用しない場合、そのバックグラウンド タスクはコネクト スタンバイ モードのとき (たとえば電話の画面がオフになっているとき) にネットワークにアクセスできません。If your background task does not use IsNetworkRequested, then your background task will not be able to access the network when in Connected Standby mode (for example, when a phone's screen is turned off).

ソケット ブローカーと SocketActivityTriggerSocket broker and the SocketActivityTrigger

アプリが DatagramSocketStreamSocket、または StreamSocketListener 接続を使う場合、SocketActivityTrigger とソケット ブローカーを使って、フォアグラウンドでないときにトラフィックがアプリに到着したという通知を受け取る必要があります。If your app uses DatagramSocket, StreamSocket, or StreamSocketListener connections, then you should use SocketActivityTrigger and the socket broker to be notified when traffic arrives for your app while it's not in the foreground.

アプリがアクティブでないときにソケットでデータを受け取って処理するには、アプリは起動時に 1 回限りのセットアップをいくつか実行した後、アクティブでない状態に移行するときにソケットの所有権をソケット ブローカーに転送する必要があります。In order for your app to receive and process data received on a socket when your app is not active, your app must perform some one-time setup at startup, and then transfer socket ownership to the socket broker when it is transitioning to a state where it is not active.

1 回限りのセットアップ手順では、次のようにトリガーを作成し、そのトリガーのバックグラウンド タスクを登録し、ソケット ブローカーのソケットを有効します。The one-time setup steps are to create a trigger, to register a background task for the trigger, and to enable the socket for the socket broker:

  • SocketActivityTrigger を作成し、TaskEntryPoint パラメーターを受信パケットを処理するためのコードに設定してトリガーのバックグラウンド タスクを登録します。Create a SocketActivityTrigger and register a background task for the trigger with the TaskEntryPoint parameter set to your code for processing a received packet.
            var socketTaskBuilder = new BackgroundTaskBuilder();
            socketTaskBuilder.Name = _backgroundTaskName;
            socketTaskBuilder.TaskEntryPoint = _backgroundTaskEntryPoint;
            var trigger = new SocketActivityTrigger();
            socketTaskBuilder.SetTrigger(trigger);
            _task = socketTaskBuilder.Register();
  • ソケットをバインドする前に、ソケットで EnableTransferOwnership を呼び出します。Call EnableTransferOwnership on the socket, before you bind the socket.
           _tcpListener = new StreamSocketListener();

           // Note that EnableTransferOwnership() should be called before bind,
           // so that tcpip keeps required state for the socket to enable connected
           // standby action. Background task Id is taken as a parameter to tie wake pattern
           // to a specific background task.  
           _tcpListener. EnableTransferOwnership(_task.TaskId,SocketActivityConnectedStandbyAction.Wake);
           _tcpListener.ConnectionReceived += OnConnectionReceived;
           await _tcpListener.BindServiceNameAsync("my-service-name");

ソケットが正しくセットアップされたら、アプリが中断する直前に、ソケットで TransferOwnership を呼び出してソケット ブローカーに転送します。Once your socket is properly set up, when your app is about to suspend, call TransferOwnership on the socket to transfer it to a socket broker. ブローカーはソケットを監視し、データが受信されたらバックグラウンド タスクをアクティブにします。The broker monitors the socket and activates your background task when data is received. 次の例には、StreamSocketListener ソケットの転送を実行する TransferOwnership ユーティリティ関数が含まれていますThe following example includes a utility TransferOwnership function to perform the transfer for StreamSocketListener sockets. (さまざまな種類の各ソケットに独自の TransferOwnership メソッドがあるため、転送する所有権を持つソケットに適したメソッドを呼び出す必要があります。(Note that the different types of sockets each have their own TransferOwnership method, so you must call the method appropriate for the socket whose ownership you are transferring. OnSuspending コードの読みやすさを維持するため、コードにはオーバーロードされた TransferOwnership ヘルパーを含め、使用するソケットの種類ごとに 1 つずつ実装することをお勧めします)。Your code would probably contain an overloaded TransferOwnership helper with one implementation for each socket type you use, so that the OnSuspending code remains easy to read.)

アプリは、次のうち適切なメソッドを使って、ソケットの所有権をソケット ブローカーに転送し、バックグラウンド タスクの ID を渡します。An app transfers ownership of a socket to a socket broker and passes the ID for the background task using the appropriate one of the following methods:


// declare int _transferOwnershipCount as a field.

private void TransferOwnership(StreamSocketListener tcpListener)
{
    await tcpListener.CancelIOAsync();

    var dataWriter = new DataWriter();
    ++_transferOwnershipCount;
    dataWriter.WriteInt32(transferOwnershipCount);
    var context = new SocketActivityContext(dataWriter.DetachBuffer());
    tcpListener.TransferOwnership(_socketId, context);
}

private void OnSuspending(object sender, SuspendingEventArgs e)
{
    var deferral = e.SuspendingOperation.GetDeferral();

    TransferOwnership(_tcpListener);
    deferral.Complete();
}

バックグラウンド タスクのイベント ハンドラーで、次の作業を行います。In your background task's event handler:

  • まず、非同期メソッドを使ってイベントを処理できるように、バックグラウンド タスクの保留を取得します。First, get a background task deferral so that you can handle the event using asynchronous methods.
var deferral = taskInstance.GetDeferral();
  • 次に、イベント引数から SocketActivityTriggerDetails を抽出し、イベントが発生した理由を調べます。Next, extract the SocketActivityTriggerDetails from the event arguments, and find the reason that the event was raised:
var details = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
    var socketInformation = details.SocketInformation;
    switch (details.Reason)
  • ソケット アクティビティが原因でイベントが発生した場合、ソケットに DataReader を作成してリーダーを非同期で読み込み、アプリのデザインに従ってデータを使います。If the event was raised because of socket activity, create a DataReader on the socket, load the reader asynchronously, and then use the data according to your app's design. ソケット アクティビティの通知を再度受け取るには、ソケットの所有権をソケット ブローカーに戻す必要がある点に注意してください。Note that you must return ownership of the socket back to the socket broker, in order to be notified of further socket activity again.

次の例では、ソケットで受け取ったテキストがトーストに表示されます。In the following example, the text received on the socket is displayed in a toast.

case SocketActivityTriggerReason.SocketActivity:
            var socket = socketInformation.StreamSocket;
            DataReader reader = new DataReader(socket.InputStream);
            reader.InputStreamOptions = InputStreamOptions.Partial;
            await reader.LoadAsync(250);
            var dataString = reader.ReadString(reader.UnconsumedBufferLength);
            ShowToast(dataString);
            socket.TransferOwnership(socketInformation.Id); /* Important! */
            break;
  • キープアライブ タイマーの有効期限が切れたためにイベントが発生した場合、ソケットをライブに維持してキープアライブ タイマーを再開するには、コードがソケット経由でデータを送信する必要があります。If the event was raised because a keep alive timer expired, then your code should send some data over the socket in order to keep the socket alive and restart the keep alive timer. 繰り返しになりますが、イベント通知を今後も受け取るには、ソケットの所有権をソケット ブローカーに戻す必要があります。Again, it is important to return ownership of the socket back to the socket broker in order to receive further event notifications:
case SocketActivityTriggerReason.KeepAliveTimerExpired:
            socket = socketInformation.StreamSocket;
            DataWriter writer = new DataWriter(socket.OutputStream);
            writer.WriteBytes(Encoding.UTF8.GetBytes("Keep alive"));
            await writer.StoreAsync();
            writer.DetachStream();
            writer.Dispose();
            socket.TransferOwnership(socketInformation.Id); /* Important! */
            break;
  • ソケットが閉じられたためにイベントが発生した場合、ソケットを再確立します。新しいソケットを作成した後、必ずその所有権をソケット ブローカーに転送します。If the event was raised because the socket was closed, re-establish the socket, making sure that after you create the new socket, you transfer ownership of it to the socket broker. 次のサンプルでは、新しいソケット接続の確立に使うことができるようにホスト名とポートがローカル設定に保存されています。In this sample, the hostname and port are stored in local settings so that they can be used to establish a new socket connection:
case SocketActivityTriggerReason.SocketClosed:
            socket = new StreamSocket();
            socket.EnableTransferOwnership(taskInstance.Task.TaskId, SocketActivityConnectedStandbyAction.Wake);
            if (ApplicationData.Current.LocalSettings.Values["hostname"] == null)
            {
                break;
            }
            var hostname = (String)ApplicationData.Current.LocalSettings.Values["hostname"];
            var port = (String)ApplicationData.Current.LocalSettings.Values["port"];
            await socket.ConnectAsync(new HostName(hostname), port);
            socket.TransferOwnership(socketId);
            break;
  • イベント通知の処理を完了したら、必ず保留を終了してください。Don't forget to Complete your deferral, once you have finished processing the event notification:
  deferral.Complete();

SocketActivityTrigger とソケット ブローカーの使い方を示す詳しいサンプルについては、SocketActivityStreamSocket のサンプルに関するページをご覧ください。For a complete sample demonstrating the use of the SocketActivityTrigger and socket broker, see the SocketActivityStreamSocket sample. Scenario1_Connect.xaml.cs ではソケットの初期化が実行され、SocketActivityTask.cs ではバックグラウンド タスクが実装されます。The initialization of the socket is performed in Scenario1_Connect.xaml.cs, and the background task implementation is in SocketActivityTask.cs.

サンプルを見ると、新しいソケットが作成されるか、既存のソケットが取得されると、すぐに TransferOwnership が呼び出されることがわかります。このトピックで説明したように OnSuspending イベント ハンドラーを使って行われるのではありません。You will probably notice that the sample calls TransferOwnership as soon as it creates a new socket or acquires an existing socket, rather than using the OnSuspending even handler to do so as described in this topic. これは、このサンプルが SocketActivityTrigger の使い方を示すことに重点を置いており、実行中に他のアクティビティにソケットを使っていないためです。This is because the sample focuses on demonstrating the SocketActivityTrigger, and doesn't use the socket for any other activity while it is running. 実際のアプリはより複雑と思われるため、OnSuspending を使って TransferOwnership を呼び出すタイミングを判断してください。Your app will probably be more complex, and should use OnSuspending to determine when to call TransferOwnership.

コントロール チャネル トリガーControl channel triggers

まず、コントロール チャネル トリガー (CCT) を適切に使っていることを確認します。First, ensure that you're using control channel triggers (CCTs) appropriately. DatagramSocketStreamSocket、または StreamSocketListener 接続を使っている場合は、SocketActivityTrigger を使うことをお勧めします。If you're using DatagramSocket, StreamSocket, or StreamSocketListener connections, then we recommend that you use SocketActivityTrigger. StreamSocket には CCT を使うことができますが、リソースを多く使うため、コネクト スタンバイ モードでは動作しない可能性があります。You can use CCTs for StreamSocket, but they use more resources and might not work in Connected Standby mode.

WebSocket、IXMLHTTPRequest2System.Net.Http.HttpClient、または Windows.Web.Http.HttpClient を使っている場合は、ControlChannelTrigger を使う必要があります。If you are using WebSockets, IXMLHTTPRequest2, System.Net.Http.HttpClient, or Windows.Web.Http.HttpClient, then you must use ControlChannelTrigger.

ControlChannelTrigger と WebSocketControlChannelTrigger with WebSockets

ControlChannelTriggerMessageWebSocket または StreamWebSocket を使う場合は、特別な注意事項がいくつかあります。Some special considerations apply when using MessageWebSocket or StreamWebSocket with ControlChannelTrigger. ControlChannelTriggerMessageWebSocket または StreamWebSocket を使う際には、トランスポート固有の使用パターンとベスト プラクティスに従う必要があります。There are some transport-specific usage patterns and best practices that should be followed when using a MessageWebSocket or StreamWebSocket with ControlChannelTrigger. また、StreamWebSocket でパケットを受け取る要求の処理方法にも、これらの注意事項が関係します。In addition, these considerations affect the way that requests to receive packets on the StreamWebSocket are handled. MessageWebSocket でパケットを受け取るための要求には影響しません。Requests to receive packets on the MessageWebSocket are not affected.

ControlChannelTriggerMessageWebSocket または StreamWebSocket を使う際に従う必要のある使用パターンとベスト プラクティスを次に示します。The following usage patterns and best practices should be followed when using MessageWebSocket or StreamWebSocket with ControlChannelTrigger:

  • 未処理のソケット受信は、常にポストされ続ける必要があります。An outstanding socket receive must be kept posted at all times. これは、プッシュ通知タスクの実行を許可するために必要です。This is required to allow the push notification tasks to occur.
  • WebSocket プロトコルで、キープアライブ メッセージの標準モデルを定義します。The WebSocket protocol defines a standard model for keep-alive messages. WebSocketKeepAlive クラスは、クライアント側から開始される WebSocket プロトコルのキープアライブ メッセージをサーバーに送信することができます。The WebSocketKeepAlive class can send client-initiated WebSocket protocol keep-alive messages to the server. WebSocketKeepAlive クラスは、アプリによって KeepAliveTrigger の TaskEntryPoint として登録される必要があります。The WebSocketKeepAlive class should be registered as the TaskEntryPoint for a KeepAliveTrigger by the app.

いくつかの特別な注意事項は、StreamWebSocket でパケットを受け取る要求の処理方法にかかわってきます。Some special considerations affect the way that requests to receive packets on the StreamWebSocket are handled. 特に、ControlChannelTriggerStreamWebSocket を使うアプリは、読み取り処理に、await モデル (C# と VB.NET) やタスク (C++) ではなく、生の非同期パターンを使う必要があります。In particular, when using a StreamWebSocket with the ControlChannelTrigger, your app must use a raw async pattern for handling reads instead of the await model in C# and VB.NET or Tasks in C++. 生の非同期パターンは、このセクションで後述するサンプル コードに示しています。The raw async pattern is illustrated in a code sample later in this section.

生の非同期パターンを使うことによって、Windows は、ControlChannelTrigger のバックグラウンド タスクの IBackgroundTask.Run メソッドを、受信完了コールバックの戻りと同期させることができます。Using the raw async pattern allows Windows to synchronize the IBackgroundTask.Run method on the background task for the ControlChannelTrigger with the return of the receive completion callback. Run メソッドは、完了コールバックから制御が戻った後に呼び出されます。The Run method is invoked after the completion callback returns. これによって、Run メソッドが呼び出される前に、アプリはデータ/エラーを確実に受け取ることができます。This ensures that the app has received the data/errors before the Run method is invoked.

アプリは、完了コールバックから制御を戻す前に別の読み取りをポストしなければならない点に注意してください。It is important to note that the app has to post another read before it returns control from the completion callback. また、MessageWebSocket または StreamWebSocket トランスポートで DataReader を直接使うことはできません。上で説明した同期に支障をきたします。It is also important to note that the DataReader cannot be directly used with the MessageWebSocket or StreamWebSocket transport since that breaks the synchronization described above. トランスポートで直接 DataReader.LoadAsync メソッドを使うことはサポートされません。It is not supported to use the DataReader.LoadAsync method directly on top of the transport. 別の方法として、StreamWebSocket.InputStream プロパティの IInputStream.ReadAsync メソッドから返された IBuffer を後で DataReader.FromBuffer メソッドに渡して処理することはできます。Instead, the IBuffer returned by the IInputStream.ReadAsync method on the StreamWebSocket.InputStream property can be later passed to DataReader.FromBuffer method for further processing.

次のサンプルでは、StreamWebSocket の読み取りを生の非同期パターンを使って処理しています。The following sample shows how to use a raw async pattern for handling reads on the StreamWebSocket.

void PostSocketRead(int length)
{
    try
    {
        var readBuf = new Windows.Storage.Streams.Buffer((uint)length);
        var readOp = socket.InputStream.ReadAsync(readBuf, (uint)length, InputStreamOptions.Partial);
        readOp.Completed = (IAsyncOperationWithProgress<IBuffer, uint>
            asyncAction, AsyncStatus asyncStatus) =>
        {
            switch (asyncStatus)
            {
                case AsyncStatus.Completed:
                case AsyncStatus.Error:
                    try
                    {
                        // GetResults in AsyncStatus::Error is called as it throws a user friendly error string.
                        IBuffer localBuf = asyncAction.GetResults();
                        uint bytesRead = localBuf.Length;
                        readPacket = DataReader.FromBuffer(localBuf);
                        OnDataReadCompletion(bytesRead, readPacket);
                    }
                    catch (Exception exp)
                    {
                        Diag.DebugPrint("Read operation failed:  " + exp.Message);
                    }
                    break;
                case AsyncStatus.Canceled:

                    // Read is not cancelled in this sample.
                    break;
           }
       };
   }
   catch (Exception exp)
   {
       Diag.DebugPrint("failed to post a read failed with error:  " + exp.Message);
   }
}

ControlChannelTrigger のバックグラウンド タスクの IBackgroundTask.Run メソッドが呼び出される前に確実に読み取り完了ハンドラーが呼び出されます。The read completion handler is guaranteed to fire before the IBackgroundTask.Run method on the background task for the ControlChannelTrigger is invoked. Windows は、読み取り完了コールバックからのアプリの復帰を待機する内部的な同期機構を備えています。Windows has internal synchronization to wait for an app to return from the read completion callback. 通常、アプリは、MessageWebSocket または StreamWebSocket からのデータやエラーを読み取り完了コールバックですぐに処理します。The app typically quickly processes the data or the error from the MessageWebSocket or StreamWebSocket in the read completion callback. メッセージそのものは、IBackgroundTask.Run メソッドのコンテキスト内で処理されます。The message itself is processed within the context of the IBackgroundTask.Run method. 以下のサンプルでは、この点をメッセージ キューを使って示しています。メッセージは、読み取り完了ハンドラーによってメッセージ キューに挿入され、バックグラウンド タスクによって後から処理されます。In this sample below, this point is illustrated by using a message queue that the read completion handler inserts the message into and the background task later processes.

次のサンプルは、StreamWebSocket の読み取りを処理するための生の非同期パターンで使う読み取り完了ハンドラーを示しています。The following sample shows the read completion handler to use with a raw async pattern for handling reads on the StreamWebSocket.

public void OnDataReadCompletion(uint bytesRead, DataReader readPacket)
{
    if (readPacket == null)
    {
        Diag.DebugPrint("DataReader is null");

        // Ideally when read completion returns error,
        // apps should be resilient and try to
        // recover if there is an error by posting another recv
        // after creating a new transport, if required.
        return;
    }
    uint buffLen = readPacket.UnconsumedBufferLength;
    Diag.DebugPrint("bytesRead: " + bytesRead + ", unconsumedbufflength: " + buffLen);

    // check if buffLen is 0 and treat that as fatal error.
    if (buffLen == 0)
    {
        Diag.DebugPrint("Received zero bytes from the socket. Server must have closed the connection.");
        Diag.DebugPrint("Try disconnecting and reconnecting to the server");
        return;
    }

    // Perform minimal processing in the completion
    string message = readPacket.ReadString(buffLen);
    Diag.DebugPrint("Received Buffer : " + message);

    // Enqueue the message received to a queue that the push notify
    // task will pick up.
    AppContext.messageQueue.Enqueue(message);

    // Post another receive to ensure future push notifications.
    PostSocketRead(MAX_BUFFER_LENGTH);
}

WebSocket に関連して、キープアライブ ハンドラーについても詳しく説明します。An additional detail for Websockets is the keep-alive handler. WebSocket プロトコルで、キープアライブ メッセージの標準モデルを定義します。The WebSocket protocol defines a standard model for keep-alive messages.

MessageWebSocket または StreamWebSocket を使う場合、アプリの中断状態を解除し、キープアライブ メッセージをサーバー (リモート エンドポイント) に定期的に送信するためには、WebSocketKeepAlive クラス インスタンスを KeepAliveTrigger の TaskEntryPoint として登録する必要があります。When using MessageWebSocket or StreamWebSocket, register a WebSocketKeepAlive class instance as the TaskEntryPoint for a KeepAliveTrigger to allow the app to be unsuspended and send keep-alive messages to the server (remote endpoint) periodically. これは、アプリのバックグラウンド登録コードとパッケージ マニフェストで行う必要があります。This should be done as part of the background registration app code as well as in the package manifest.

Windows.Sockets.WebSocketKeepAlive のタスク エントリ ポイントは、次の 2 か所で指定する必要があります。This task entry point of Windows.Sockets.WebSocketKeepAlive needs to be specified in two places:

  • ソース コードで KeepAliveTrigger トリガーを作成する部分 (以下の例を参照)。When creating KeepAliveTrigger trigger in the source code (see example below).
  • キープアライブのバックグラウンド タスクが宣言されているアプリ パッケージ マニフェスト。In the app package manifest for the keepalive background task declaration.

次のサンプルでは、アプリ マニフェストの <Application> 要素の下に、ネットワーク トリガー通知とキープアライブ トリガーを追加しています。The following sample adds a network trigger notification and a keepalive trigger under the <Application> element in an app manifest.

  <Extensions>
    <Extension Category="windows.backgroundTasks"
         Executable="$targetnametoken$.exe"
         EntryPoint="Background.PushNotifyTask">
      <BackgroundTasks>
        <Task Type="controlChannel" />
      </BackgroundTasks>
    </Extension>
    <Extension Category="windows.backgroundTasks"
         Executable="$targetnametoken$.exe"
         EntryPoint="Windows.Networking.Sockets.WebSocketKeepAlive">
      <BackgroundTasks>
        <Task Type="controlChannel" />
      </BackgroundTasks>
    </Extension>
  </Extensions>

ControlChannelTriggerStreamWebSocketMessageWebSocket、または StreamSocket に対する非同期操作のコンテキストで await ステートメントを使う際には、特に注意する必要があります。An app must be extremely careful when using an await statement in the context of a ControlChannelTrigger and an asynchronous operation on a StreamWebSocket, MessageWebSocket, or StreamSocket. TaskTask<bool> オブジェクトを使うと、プッシュ通知と WebSocket キープアライブの ControlChannelTriggerStreamWebSocket に対して登録し、このトランスポートを接続できます。A Task<bool> object can be used to register a ControlChannelTrigger for push notification and WebSocket keep-alives on the StreamWebSocket and connect the transport. この登録の一環として、StreamWebSocket トランスポートが ControlChannelTrigger のトランスポートとして設定され、読み取りがポストされます。As part of the registration, the StreamWebSocket transport is set as the transport for the ControlChannelTrigger and a read is posted. Task.Result は、タスクのすべてのステップが実行されてメッセージ本文でステートメントが返されるまで現在のスレッドをブロックします。The Task.Result will block the current thread until all steps in the task execute and return statements in message body. タスクは、このメソッドが true または false を返すまで解決されません。The task is not resolved until the method returns either true or false. これにより、メソッド全体が実行されることが保証されます。This guarantees that the whole method is executed. Task には、Task によって保護される await ステートメントを複数含めることができます。The Task can contain multiple await statements that are protected by the Task. ControlChannelTrigger オブジェクトで StreamWebSocket または MessageWebSocket をトランスポートとして使う場合はこのパターンを使う必要があります。This pattern should be used with the ControlChannelTrigger object when a StreamWebSocket or MessageWebSocket is used as the transport. 完了までに長時間かかる可能性がある操作 (一般的な非同期読み取り操作など) に対しては、既に説明した生の非同期パターンを使う必要があります。For those operations that may take a long period of time to complete (a typical async read operation, for example), the app should use the raw async pattern discussed previously.

次のサンプルは、プッシュ通知と WebSocket キープアライブの ControlChannelTriggerStreamWebSocket に対して登録します。The following sample registers ControlChannelTrigger for push notification and WebSocket keep-alives on the StreamWebSocket.

private bool RegisterWithControlChannelTrigger(string serverUri)
{
    // Make sure the objects are created in a system thread
    // Demonstrate the core registration path
    // Wait for the entire operation to complete before returning from this method.
    // The transport setup routine can be triggered by user control, by network state change
    // or by keepalive task
    Task<bool> registerTask = RegisterWithCCTHelper(serverUri);
    return registerTask.Result;
}

async Task<bool> RegisterWithCCTHelper(string serverUri)
{
    bool result = false;
    socket = new StreamWebSocket();

    // Specify the keepalive interval expected by the server for this app
    // in order of minutes.
    const int serverKeepAliveInterval = 30;

    // Specify the channelId string to differentiate this
    // channel instance from any other channel instance.
    // When background task fires, the channel object is provided
    // as context and the channel id can be used to adapt the behavior
    // of the app as required.
    const string channelId = "channelOne";

    // For websockets, the system does the keepalive on behalf of the app
    // But the app still needs to specify this well known keepalive task.
    // This should be done here in the background registration as well
    // as in the package manifest.
    const string WebSocketKeepAliveTask = "Windows.Networking.Sockets.WebSocketKeepAlive";

    // Try creating the controlchanneltrigger if this has not been already
    // created and stored in the property bag.
    ControlChannelTriggerStatus status;

    // Create the ControlChannelTrigger object and request a hardware slot for this app.
    // If the app is not on LockScreen, then the ControlChannelTrigger constructor will
    // fail right away.
    try
    {
        channel = new ControlChannelTrigger(channelId, serverKeepAliveInterval,
                                   ControlChannelTriggerResourceType.RequestHardwareSlot);
    }
    catch (UnauthorizedAccessException exp)
    {
        Diag.DebugPrint("Is the app on lockscreen? " + exp.Message);
        return result;
    }

    Uri serverUriInstance;
    try
    {
        serverUriInstance = new Uri(serverUri);
    }
    catch (Exception exp)
    {
        Diag.DebugPrint("Error creating URI: " + exp.Message);
        return result;
    }

    // Register the apps background task with the trigger for keepalive.
    var keepAliveBuilder = new BackgroundTaskBuilder();
    keepAliveBuilder.Name = "KeepaliveTaskForChannelOne";
    keepAliveBuilder.TaskEntryPoint = WebSocketKeepAliveTask;
    keepAliveBuilder.SetTrigger(channel.KeepAliveTrigger);
    keepAliveBuilder.Register();

    // Register the apps background task with the trigger for push notification task.
    var pushNotifyBuilder = new BackgroundTaskBuilder();
    pushNotifyBuilder.Name = "PushNotificationTaskForChannelOne";
    pushNotifyBuilder.TaskEntryPoint = "Background.PushNotifyTask";
    pushNotifyBuilder.SetTrigger(channel.PushNotificationTrigger);
    pushNotifyBuilder.Register();

    // Tie the transport method to the ControlChannelTrigger object to push enable it.
    // Note that if the transport' s TCP connection is broken at a later point of time,
    // the ControlChannelTrigger object can be reused to plug in a new transport by
    // calling UsingTransport API again.
    try
    {
        channel.UsingTransport(socket);

        // Connect the socket
        //
        // If connect fails or times out it will throw exception.
        // ConnectAsync can also fail if hardware slot was requested
        // but none are available
        await socket.ConnectAsync(serverUriInstance);

        // Call WaitForPushEnabled API to make sure the TCP connection has
        // been established, which will mean that the OS will have allocated
        // any hardware slot for this TCP connection.
        //
        // In this sample, the ControlChannelTrigger object was created by
        // explicitly requesting a hardware slot.
        //
        // On systems that without connected standby, if app requests hardware slot as above,
        // the system will fallback to a software slot automatically.
        //
        // On systems that support connected standby,, if no hardware slot is available, then app
        // can request a software slot by re-creating the ControlChannelTrigger object.
        status = channel.WaitForPushEnabled();
        if (status != ControlChannelTriggerStatus.HardwareSlotAllocated
            && status != ControlChannelTriggerStatus.SoftwareSlotAllocated)
        {
            throw new Exception(string.Format("Neither hardware nor software slot could be allocated. ChannelStatus is {0}", status.ToString()));
        }

        // Store the objects created in the property bag for later use.
        CoreApplication.Properties.Remove(channel.ControlChannelTriggerId);

        var appContext = new AppContext(this, socket, channel, channel.ControlChannelTriggerId);
        ((IDictionary<string, object>)CoreApplication.Properties).Add(channel.ControlChannelTriggerId, appContext);
        result = true;

        // Almost done. Post a read since we are using streamwebsocket
        // to allow push notifications to be received.
        PostSocketRead(MAX_BUFFER_LENGTH);
    }
    catch (Exception exp)
    {
         Diag.DebugPrint("RegisterWithCCTHelper Task failed with: " + exp.Message);

         // Exceptions may be thrown for example if the application has not
         // registered the background task class id for using real time communications
         // broker in the package manifest.
    }
    return result
}

ControlChannelTriggerMessageWebSocket または StreamWebSocket を使う方法について詳しくは、ControlChannelTrigger StreamWebSocket のサンプルに関するページをご覧ください。For more information on using MessageWebSocket or StreamWebSocket with ControlChannelTrigger, see the ControlChannelTrigger StreamWebSocket sample.

ControlChannelTrigger と HttpClientControlChannelTrigger with HttpClient

ControlChannelTriggerHttpClient を使う場合は、特別な注意事項がいくつかあります。Some special considerations apply when using HttpClient with ControlChannelTrigger. ControlChannelTriggerHttpClient を使う際には、トランスポート固有の使用パターンとベスト プラクティスに従う必要があります。There are some transport-specific usage patterns and best practices that should be followed when using a HttpClient with ControlChannelTrigger. また、HttpClient でパケットを受信する要求の処理方法にも、これらの注意事項が関係します。In addition, these considerations affect the way that requests to receive packets on the HttpClient are handled.

SSL を使う   HttpClient では、ネットワーク トリガー機能と ControlChannelTrigger の使用は現在サポートされていません。Note  HttpClient using SSL is not currently supported using the network trigger feature and ControlChannelTrigger.   ControlChannelTriggerHttpClient を使う際に従う必要のある使用パターンとベスト プラクティスを次に示します。The following usage patterns and best practices should be followed when using HttpClient with ControlChannelTrigger:

  • アプリで、特定の URI に要求を送る前に、System.Net.Http 名前空間の HttpClient オブジェクトまたは HttpClientHandler オブジェクトにさまざまなプロパティやヘッダーを設定する必要がある場合があります。The app may need to set various properties and headers on the HttpClient or HttpClientHandler object in the System.Net.Http namespace before sending the request to the specific URI.
  • アプリには、ControlChannelTrigger で使う HttpClient トランスポートを作る前に、トランスポートをテストし、正しく設定するための初期要求が必要な場合があります。An app may need to make need to an initial request to test and setup the transport properly before creating the HttpClient transport to be used with ControlChannelTrigger. トランスポートを正しく設定できることがアプリによって確認されると、HttpClient オブジェクトを ControlChannelTrigger オブジェクトで使うトランスポート オブジェクトとして構成できます。Once the app determines that the transport can be properly setup, an HttpClient object can be configured as the transport object used with the ControlChannelTrigger object. このプロセスは、一部のシナリオでトランスポートを使って確立された接続が中断されないようにするためのものです。This process is designed prevent some scenarios from breaking the connection established over the transport. SSL と証明書を使う場合、アプリには、PIN 入力用、または選択する証明書が複数ある場合に表示されるダイアログが必要になる場合があります。Using SSL with a certificate, an app may require a dialog to be displayed for PIN entry or if there are multiple certificates to choose from. プロキシ認証とサーバー認証が必要になる場合があります。Proxy authentication and server authentication may be required. プロキシ認証またはサーバー認証の期限が切れると、接続が閉じる場合があります。If the proxy or server authentication expires, the connection may be closed. これらの認証期限の問題に対処する 1 つの方法として、タイマーを設定できます。One way an app can deal with these authentication expiration issues is to set a timer. HTTP リダイレクトが必要な場合、2 回目の接続は正しく確立できないことがあります。When an HTTP redirect is required, it is not guaranteed that the second connection can be established reliably. 初期テスト要求により、HttpClient オブジェクトを ControlChannelTrigger オブジェクトでトランスポートとして使う前にアプリが最新のリダイレクト URL を使用できることが確認されます。An initial test request will ensure that the app can use the most up-to-date redirected URL before using the HttpClient object as the transport with the ControlChannelTrigger object.

他のネットワーク トランスポートとは異なり、ControlChannelTrigger オブジェクトの UsingTransport メソッドに直接 HttpClient オブジェクトを渡すことはできません。Unlike other network transports, the HttpClient object cannot be directly passed into the UsingTransport method of the ControlChannelTrigger object. HttpClient オブジェクトと ControlChannelTrigger 用に特別な方法で HttpRequestMessage オブジェクトを構築する必要があります。Instead, an HttpRequestMessage object must be specially constructed for use with the HttpClient object and the ControlChannelTrigger. HttpRequestMessage オブジェクトは、RtcRequestFactory.Create メソッドを使って作成します。The HttpRequestMessage object is created using the RtcRequestFactory.Create method. そのうえで、作成した HttpRequestMessage オブジェクトを UsingTransport メソッドに渡します。The HttpRequestMessage object that is created is then passed to UsingTransport method.

次のサンプルでは、HttpClient オブジェクトと ControlChannelTrigger で使う HttpRequestMessage オブジェクトを構築しています。The following sample shows how to construct an HttpRequestMessage object for use with the HttpClient object and the ControlChannelTrigger.

using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking.Sockets;

public HttpRequestMessage httpRequest;
public HttpClient httpClient;
public HttpRequestMessage httpRequest;
public ControlChannelTrigger channel;
public Uri serverUri;

private void SetupHttpRequestAndSendToHttpServer()
{
    try
    {
        // For HTTP based transports that use the RTC broker, whenever we send next request, we will abort the earlier
        // outstanding http request and start new one.
        // For example in case when http server is taking longer to reply, and keep alive trigger is fired in-between
        // then keep alive task will abort outstanding http request and start a new request which should be finished
        // before next keep alive task is triggered.
        if (httpRequest != null)
        {
            httpRequest.Dispose();
        }

        httpRequest = RtcRequestFactory.Create(HttpMethod.Get, serverUri);

        SendHttpRequest();
    }
        catch (Exception e)
    {
        Diag.DebugPrint("Connect failed with: " + e.ToString());
        throw;
    }
}

いくつかの特別な注意事項は、HttpClient で HTTP 要求を送信して応答の受け取りを開始するための要求の処理方法にかかわってきます。Some special considerations affect the way that requests to send HTTP requests on the HttpClient to initiate receiving a response are handled. 特に、ControlChannelTriggerHttpClient を使うアプリは、送信処理に、await モデルではなく、Task を使う必要があります。In particular, when using a HttpClient with the ControlChannelTrigger, your app must use a Task for handling sends instead of the await model.

HttpClient を使った場合、ControlChannelTrigger のバックグラウンド タスクの IBackgroundTask.Run メソッドと、受信完了コールバックの戻りとの同期が生じません。Using HttpClient, there is no synchronization with the IBackgroundTask.Run method on the background task for the ControlChannelTrigger with the return of the receive completion callback. したがって、アプリは、ブロックする HttpResponseMessage を Run メソッドで使い、応答全体を受け取るまで待機するしかありません。For this reason, the app can only use the blocking HttpResponseMessage technique in the Run method and wait until the whole response is received.

ControlChannelTrigger での HttpClient の使用は、StreamSocketMessageWebSocketStreamWebSocket のトランスポートとは大きく異なります。Using HttpClient with ControlChannelTrigger is noticeably different from the StreamSocket, MessageWebSocket or StreamWebSocket transports . HttpClient 受信コールバックは、HttpClient コード以後、Task を介してアプリに送られます。The HttpClient receive callback is delivered via a Task to the app since the HttpClient code. つまり、データまたはエラーがアプリにディスパッチされるとすぐに ControlChannelTrigger プッシュ通知タスクが作動します。This means that the ControlChannelTrigger push notification task will fire as soon as the data or error is dispatched to the app. 以下のサンプル コードは、HttpClient.SendAsync メソッドから返された responseTask をグローバルなストレージに格納します。プッシュ通知タスクがそれを取り出し、インラインで処理します。In the sample below, the code stores the responseTask returned by HttpClient.SendAsync method into global storage that the push notify task will pick up and process inline.

次のサンプルは、HttpClientControlChannelTrigger と組み合わせて使い、送信要求を処理しています。The following sample shows how to handle send requests on the HttpClient when used with ControlChannelTrigger.

using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking.Sockets;

private void SendHttpRequest()
{
    if (httpRequest == null)
    {
        throw new Exception("HttpRequest object is null");
    }

    // Tie the transport method to the controlchanneltrigger object to push enable it.
    // Note that if the transport' s TCP connection is broken at a later point of time,
    // the controlchanneltrigger object can be reused to plugin a new transport by
    // calling UsingTransport API again.
    channel.UsingTransport(httpRequest);

    // Call the SendAsync function to kick start the TCP connection establishment
    // process for this http request.
    Task<HttpResponseMessage> httpResponseTask = httpClient.SendAsync(httpRequest);

    // Call WaitForPushEnabled API to make sure the TCP connection has been established,
    // which will mean that the OS will have allocated any hardware slot for this TCP connection.
    ControlChannelTriggerStatus status = channel.WaitForPushEnabled();
    Diag.DebugPrint("WaitForPushEnabled() completed with status: " + status);
    if (status != ControlChannelTriggerStatus.HardwareSlotAllocated
        && status != ControlChannelTriggerStatus.SoftwareSlotAllocated)
    {
        throw new Exception("Hardware/Software slot not allocated");
    }

    // The HttpClient receive callback is delivered via a Task to the app.
    // The notification task will fire as soon as the data or error is dispatched
    // Enqueue the responseTask returned by httpClient.sendAsync
    // into a queue that the push notify task will pick up and process inline.
    AppContext.messageQueue.Enqueue(httpResponseTask);
}

次のサンプルは、HttpClientControlChannelTrigger と組み合わせて使い、受け取った応答を読み取っています。The following sample shows how to read responses received on the HttpClient when used with ControlChannelTrigger.

using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public string ReadResponse(Task<HttpResponseMessage> httpResponseTask)
{
    string message = null;
    try
    {
        if (httpResponseTask.IsCanceled || httpResponseTask.IsFaulted)
        {
            Diag.DebugPrint("Task is cancelled or has failed");
            return message;
        }
        // We' ll wait until we got the whole response.
        // This is the only supported scenario for HttpClient for ControlChannelTrigger.
        HttpResponseMessage httpResponse = httpResponseTask.Result;
        if (httpResponse == null || httpResponse.Content == null)
        {
            Diag.DebugPrint("Cannot read from httpresponse, as either httpResponse or its content is null. try to reset connection.");
        }
        else
        {
            // This is likely being processed in the context of a background task and so
            // synchronously read the Content' s results inline so that the Toast can be shown.
            // before we exit the Run method.
            message = httpResponse.Content.ReadAsStringAsync().Result;
        }
    }
    catch (Exception exp)
    {
        Diag.DebugPrint("Failed to read from httpresponse with error:  " + exp.ToString());
    }
    return message;
}

HttpClientControlChannelTrigger を使う方法について詳しくは、ControlChannelTrigger HttpClient のサンプルに関するページをご覧ください。For more information on using HttpClient with ControlChannelTrigger, see the ControlChannelTrigger HttpClient sample.

ControlChannelTrigger と IXMLHttpRequest2ControlChannelTrigger with IXMLHttpRequest2

ControlChannelTriggerIXMLHTTPRequest2 を使う場合は、特別な注意事項がいくつかあります。Some special considerations apply when using IXMLHTTPRequest2 with ControlChannelTrigger. ControlChannelTriggerIXMLHTTPRequest2 を使う際には、トランスポート固有の使用パターンとベスト プラクティスに従う必要があります。There are some transport-specific usage patterns and best practices that should be followed when using a IXMLHTTPRequest2 with ControlChannelTrigger. ControlChannelTrigger の使用が、IXMLHTTPRequest2 で HTTP 要求を送受信するための要求の処理方法に影響することはありません。Using ControlChannelTrigger does not affect the way that requests to send or receive HTTP requests on the IXMLHTTPRequest2 are handled.

ControlChannelTriggerIXMLHTTPRequest2 を使う際の使用パターンとベスト プラクティスUsage patterns and best practices when using IXMLHTTPRequest2 with ControlChannelTrigger

  • トランスポートとして使われる IXMLHTTPRequest2 オブジェクトの有効期限は、1 つの要求/応答のみで構成されます。An IXMLHTTPRequest2 object when used as the transport has a lifetime of only one request/response. ControlChannelTrigger オブジェクトと併用する場合は、ControlChannelTrigger オブジェクトを一度作って設定してから UsingTransport メソッドを繰り返し呼び出して、そのたびに新しい IXMLHTTPRequest2 オブジェクトを関連付けると便利です。When used with the ControlChannelTrigger object, it is convenient to create and set up the ControlChannelTrigger object once and then call the UsingTransport method repeatedly, each time associating a new IXMLHTTPRequest2 object. アプリは、新しい IXMLHTTPRequest2 オブジェクトを提供する前に以前の IXMLHTTPRequest2 オブジェクトを削除して、割り当てられたリソース制限を超えないようにする必要があります。An app should delete the previous IXMLHTTPRequest2 object before supplying a new IXMLHTTPRequest2 object to ensure that the app does not exceed the allocated resource limits.
  • アプリは、Send メソッドを呼び出す前に、SetProperty メソッドと SetRequestHeader メソッドを呼び出して HTTP トランスポートを設定する必要がある場合があります。The app may need to call the SetProperty and SetRequestHeader methods to set up the HTTP transport before calling Send method.
  • アプリには、ControlChannelTrigger で使うトランスポートを作る前に、トランスポートをテストし、正しく設定するための初期 Send 要求が必要な場合があります。An app may need to make need to an initial Send request to test and setup the transport properly before creating the transport to be used with ControlChannelTrigger. アプリによってトランスポートが正しく設定されていることが確認されると、IXMLHTTPRequest2 オブジェクトを ControlChannelTrigger で使うトランスポート オブジェクトとして構成できます。Once the app determines that the transport is properly setup, the IXMLHTTPRequest2 object can be configured as the transport object used with the ControlChannelTrigger. このプロセスは、一部のシナリオでトランスポートを使って確立された接続が中断されないようにするためのものです。This process is designed prevent some scenarios from breaking the connection established over the transport. SSL と証明書を使う場合、アプリには、PIN 入力用、または選択する証明書が複数ある場合に表示されるダイアログが必要になる場合があります。Using SSL with a certificate, an app may require a dialog to be displayed for PIN entry or if there are multiple certificates to choose from. プロキシ認証とサーバー認証が必要になる場合があります。Proxy authentication and server authentication may be required. プロキシ認証またはサーバー認証の期限が切れると、接続が閉じる場合があります。If the proxy or server authentication expires, the connection may be closed. これらの認証期限の問題に対処する 1 つの方法として、タイマーを設定できます。One way an app can deal with these authentication expiration issues is to set a timer. HTTP リダイレクトが必要な場合、2 回目の接続は正しく確立できないことがあります。When an HTTP redirect is required, it is not guaranteed that the second connection can be established reliably. 初期テスト要求により、IXMLHTTPRequest2 オブジェクトを ControlChannelTrigger オブジェクトでトランスポートとして使う前にアプリが最新のリダイレクト URL を使用できることが確認されます。An initial test request will ensure that the app can use the most up-to-date redirected URL before using the IXMLHTTPRequest2 object as the transport with the ControlChannelTrigger object.

IXMLHTTPRequest2ControlChannelTrigger を使う方法について詳しくは、ControlChannelTrigger と IXMLHTTPRequest2 のサンプルに関するページをご覧ください。For more information on using IXMLHTTPRequest2 with ControlChannelTrigger, see the ControlChannelTrigger with IXMLHTTPRequest2 sample.

重要な APIImportant APIs