Komunikasi jaringan di latar belakang

Untuk melanjutkan komunikasi jaringan saat tidak berada di latar depan, aplikasi Anda dapat menggunakan tugas latar belakang dan salah satu dari dua opsi ini.

  • Broker soket. Jika aplikasi Anda menggunakan soket untuk koneksi jangka panjang, ketika meninggalkan latar depan, aplikasi tersebut dapat mendelegasikan kepemilikan soket ke broker soket sistem. Broker kemudian: mengaktifkan aplikasi Anda ketika lalu lintas tiba di soket; mentransfer kepemilikan kembali ke aplikasi Anda; dan aplikasi Anda kemudian memproses lalu lintas yang tiba.
  • Mengontrol pemicu saluran.

Melakukan operasi jaringan dalam tugas latar belakang

  • Gunakan SocketActivityTrigger untuk mengaktifkan tugas latar belakang saat paket diterima dan Anda perlu melakukan tugas berumur pendek. Setelah melakukan tugas, tugas latar belakang harus dihentikan untuk menghemat daya.
  • Gunakan ControlChannelTrigger untuk mengaktifkan tugas latar belakang saat paket diterima dan Anda perlu melakukan tugas berumur panjang.

Kondisi dan bendera terkait jaringan

  • Tambahkan kondisi InternetAvailable ke tugas latar belakang Anda BackgroundTaskBuilder.AddCondition untuk menunda pemicu tugas latar belakang hingga tumpukan jaringan berjalan. Kondisi ini menghemat daya karena tugas latar belakang tidak akan dijalankan hingga jaringan aktif. Kondisi ini tidak memberikan aktivasi real-time.

Terlepas dari pemicu yang Anda gunakan, atur IsNetworkRequested pada tugas latar belakang Anda untuk memastikan bahwa jaringan tetap aktif saat tugas latar belakang berjalan. Ini memberi tahu infrastruktur tugas latar belakang untuk menjaga jaringan tetap aktif saat tugas dijalankan, bahkan jika perangkat telah memasuki mode Siaga Koneksi. Jika tugas latar belakang Anda tidak menggunakan IsNetworkRequested, maka tugas latar belakang Anda tidak akan dapat mengakses jaringan ketika dalam mode Siaga Koneksi (misalnya, ketika layar ponsel dimatikan).

Broker soket dan SocketActivityTrigger

Jika aplikasi Anda menggunakan koneksi DatagramSocket, StreamSocket, atau StreamSocketListener , maka Anda harus menggunakan SocketActivityTrigger dan broker soket untuk diberi tahu saat lalu lintas tiba untuk aplikasi Anda saat tidak berada di latar depan.

Agar aplikasi Anda menerima dan memproses data yang diterima pada soket saat aplikasi Anda tidak aktif, aplikasi Anda harus melakukan beberapa penyiapan satu kali saat startup, lalu mentransfer kepemilikan soket ke broker soket saat bertransisi ke status di mana aplikasi tidak aktif.

Langkah-langkah penyiapan satu kali adalah membuat pemicu, untuk mendaftarkan tugas latar belakang untuk pemicu, dan untuk mengaktifkan soket untuk broker soket:

  • Buat SocketActivityTrigger dan daftarkan tugas latar belakang untuk pemicu dengan parameter TaskEntryPoint yang diatur ke kode Anda untuk memproses paket yang diterima.
            var socketTaskBuilder = new BackgroundTaskBuilder();
            socketTaskBuilder.Name = _backgroundTaskName;
            socketTaskBuilder.TaskEntryPoint = _backgroundTaskEntryPoint;
            var trigger = new SocketActivityTrigger();
            socketTaskBuilder.SetTrigger(trigger);
            _task = socketTaskBuilder.Register();
  • Panggil EnableTransferOwnership pada soket, sebelum Anda mengikat soket.
           _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");

Setelah soket Anda disiapkan dengan benar, saat aplikasi Anda akan ditangguhkan, hubungi TransferOwnership di soket untuk mentransfernya ke broker soket. Broker memantau soket dan mengaktifkan tugas latar belakang Anda saat data diterima. Contoh berikut mencakup fungsi TransferOwnership utilitas untuk melakukan transfer untuk soket StreamSocketListener. (Perhatikan bahwa berbagai jenis soket masing-masing memiliki Metode TransferOwnership, jadi Anda harus memanggil metode yang sesuai untuk soket yang kepemilikannya Anda transfer. Kode Anda mungkin akan berisi pembantu TransferOwnership yang kelebihan beban dengan satu implementasi untuk setiap jenis soket yang Anda gunakan, sehingga kode OnSuspending tetap mudah dibaca.)

Aplikasi mentransfer kepemilikan soket ke broker soket dan meneruskan ID untuk tugas latar belakang menggunakan salah satu metode berikut yang sesuai:


// 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();
}

Di penanganan aktivitas tugas latar belakang Anda:

  • Pertama, dapatkan penangguhan tugas latar belakang sehingga Anda dapat menangani peristiwa menggunakan metode asinkron.
var deferral = taskInstance.GetDeferral();
  • Selanjutnya, ekstrak SocketActivityTriggerDetails dari argumen peristiwa, dan temukan alasan peristiwa dimunculkan:
var details = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
    var socketInformation = details.SocketInformation;
    switch (details.Reason)
  • Jika peristiwa dinaikkan karena aktivitas soket, buat DataReader di soket, muat pembaca secara asinkron, lalu gunakan data sesuai dengan desain aplikasi Anda. Perhatikan bahwa Anda harus mengembalikan kepemilikan soket kembali ke broker soket, untuk diberi tahu tentang aktivitas soket lebih lanjut lagi.

Dalam contoh berikut, teks yang diterima pada soket ditampilkan dalam roti panggang.

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;
  • Jika peristiwa dinaikkan karena timer tetap hidup kedaluwarsa, maka kode Anda harus mengirim beberapa data melalui soket untuk menjaga soket tetap hidup dan memulai ulang timer tetap hidup. Sekali lagi, penting untuk mengembalikan kepemilikan soket kembali ke broker soket untuk menerima pemberitahuan peristiwa lebih lanjut:
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;
  • Jika peristiwa dinaikkan karena soket ditutup, buat ulang soket, pastikan setelah Anda membuat soket baru, Anda mentransfer kepemilikannya ke broker soket. Dalam sampel ini, nama host dan port disimpan dalam pengaturan lokal sehingga dapat digunakan untuk membuat koneksi soket baru:
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;
  • Jangan lupa untuk Menyelesaikan penangguhan Anda, setelah Anda selesai memproses pemberitahuan peristiwa:
  deferral.Complete();

Untuk sampel lengkap yang menunjukkan penggunaan SocketActivityTrigger dan broker soket, lihat sampel SocketActivityStreamSocket. Inisialisasi soket dilakukan di Scenario1_Koneksi.xaml.cs, dan implementasi tugas latar belakang ada di SocketActivityTask.cs.

Anda mungkin akan melihat bahwa sampel memanggil TransferOwnership segera setelah membuat soket baru atau memperoleh soket yang ada, daripada menggunakan handler OnSuspending bahkan untuk melakukannya seperti yang dijelaskan dalam topik ini. Ini karena sampel berfokus pada menunjukkan SocketActivityTrigger, dan tidak menggunakan soket untuk aktivitas lain saat sedang berjalan. Aplikasi Anda mungkin akan lebih kompleks, dan harus menggunakan OnSuspending untuk menentukan kapan harus memanggil TransferOwnership.

Mengontrol pemicu saluran

Pertama, pastikan Anda menggunakan pemicu saluran kontrol (CCT) dengan tepat. Jika Anda menggunakan koneksi DatagramSocket, StreamSocket, atau StreamSocketListener, kami sarankan Anda menggunakan SocketActivityTrigger. Anda dapat menggunakan CCT untuk StreamSocket, tetapi mereka menggunakan lebih banyak sumber daya dan mungkin tidak berfungsi dalam mode Siaga Koneksi.

Jika Anda menggunakan WebSockets, IXMLHTTPRequest2, System.Net.Http.HttpClient, atau Windows.Web.Http.HttpClient, maka Anda harus menggunakan ControlChannelTrigger.

ControlChannelTrigger dengan WebSockets

Penting

Fitur yang dijelaskan di bagian ini (ControlChannelTrigger dengan WebSockets) didukung di SDK versi 10.0.15063.0, dan yang lebih lama. Ini juga didukung dalam versi pra-rilis Dari Windows 10 Insider Preview.

Beberapa pertimbangan khusus berlaku saat menggunakan MessageWebSocket atau StreamWebSocket dengan ControlChannelTrigger. Ada beberapa pola penggunaan khusus transportasi dan praktik terbaik yang harus diikuti saat menggunakan MessageWebSocket atau StreamWebSocket dengan ControlChannelTrigger. Selain itu, pertimbangan ini memengaruhi cara permintaan untuk menerima paket di StreamWebSocket ditangani. Permintaan untuk menerima paket di MessageWebSocket tidak terpengaruh.

Pola penggunaan dan praktik terbaik berikut harus diikuti saat menggunakan MessageWebSocket atau StreamWebSocket dengan ControlChannelTrigger:

  • Penerimaan soket yang luar biasa harus terus diposting setiap saat. Ini diperlukan untuk memungkinkan tugas pemberitahuan push terjadi.
  • Protokol WebSocket mendefinisikan model standar untuk pesan tetap aktif. Kelas WebSocketKeepAlive dapat mengirim pesan protokol WebSocket yang dimulai klien tetap aktif ke server. Kelas WebSocketKeepAlive harus didaftarkan sebagai TaskEntryPoint untuk KeepAliveTrigger oleh aplikasi.

Beberapa pertimbangan khusus memengaruhi cara permintaan untuk menerima paket di StreamWebSocket ditangani. Secara khusus, saat menggunakan StreamWebSocket dengan ControlChannelTrigger, aplikasi Anda harus menggunakan pola asinkron mentah untuk menangani bacaan alih-alih menunggu model di C# dan VB.NET atau Tugas di C++. Pola asinkron mentah diilustrasikan dalam sampel kode nanti di bagian ini.

Menggunakan pola asinkron mentah memungkinkan Windows untuk menyinkronkan metode IBackgroundTask.Run pada tugas latar belakang untuk ControlChannelTrigger dengan pengembalian panggilan balik penyelesaian penerimaan. Metode Jalankan dipanggil setelah panggilan balik penyelesaian kembali. Ini memastikan bahwa aplikasi telah menerima data/kesalahan sebelum metode Jalankan dipanggil.

Penting untuk dicatat bahwa aplikasi harus memposting bacaan lain sebelum mengembalikan kontrol dari panggilan balik penyelesaian. Penting juga untuk dicatat bahwa DataReader tidak dapat langsung digunakan dengan transportasi MessageWebSocket atau StreamWebSocket karena itu merusak sinkronisasi yang dijelaskan di atas. Tidak didukung untuk menggunakan metode DataReader.LoadAsync langsung di atas transportasi. Sebagai gantinya, IBuffer yang dikembalikan oleh metode IInputStream.ReadAsync pada properti StreamWebSocket.InputStream nantinya dapat diteruskan ke metode DataReader.FromBuffer untuk pemrosesan lebih lanjut.

Sampel berikut menunjukkan cara menggunakan pola asinkron mentah untuk menangani bacaan di 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);
   }
}

Handler penyelesaian baca dijamin diaktifkan sebelum metode IBackgroundTask.Run pada tugas latar belakang untuk ControlChannelTrigger dipanggil. Windows memiliki sinkronisasi internal untuk menunggu aplikasi kembali dari panggilan balik penyelesaian baca. Aplikasi ini biasanya dengan cepat memproses data atau kesalahan dari MessageWebSocket atau StreamWebSocket dalam panggilan balik penyelesaian baca. Pesan itu sendiri diproses dalam konteks metode IBackgroundTask.Run . Dalam sampel ini di bawah ini, titik ini diilustrasikan dengan menggunakan antrean pesan bahwa handler penyelesaian baca menyisipkan pesan ke dalam dan tugas latar belakang kemudian diproses.

Sampel berikut menunjukkan handler penyelesaian baca untuk digunakan dengan pola asinkron mentah untuk menangani bacaan di 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);
}

Detail tambahan untuk Websockets adalah handler tetap aktif. Protokol WebSocket mendefinisikan model standar untuk pesan tetap aktif.

Saat menggunakan MessageWebSocket atau StreamWebSocket, daftarkan instans kelas WebSocketKeepAlive sebagai TaskEntryPoint untuk KeepAliveTrigger untuk memungkinkan aplikasi tidak digunakan dan mengirim pesan tetap hidup ke server (titik akhir jarak jauh) secara berkala. Ini harus dilakukan sebagai bagian dari kode aplikasi pendaftaran latar belakang serta dalam manifes paket.

Titik entri tugas Windows.Sockets.WebSocketKeepAlive ini perlu ditentukan di dua tempat:

  • Saat membuat pemicu KeepAliveTrigger dalam kode sumber (lihat contoh di bawah).
  • Dalam manifes paket aplikasi untuk deklarasi tugas latar belakang keepalive.

Sampel berikut menambahkan pemberitahuan pemicu jaringan dan pemicu keepalive di bawah <elemen Aplikasi> dalam manifes aplikasi.

  <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>

Aplikasi harus sangat berhati-hati saat menggunakan pernyataan tunggu dalam konteks ControlChannelTrigger dan operasi asinkron pada StreamWebSocket, MessageWebSocket, atau StreamSocket. Objek bool> Tugas<dapat digunakan untuk mendaftarkan ControlChannelTrigger untuk pemberitahuan push dan WebSocket tetap aktif di StreamWebSocket dan menghubungkan transportasi. Sebagai bagian dari pendaftaran, transportasi StreamWebSocket ditetapkan sebagai transportasi untuk ControlChannelTrigger dan bacaan diposting. Task.Result akan memblokir utas saat ini hingga semua langkah dalam tugas dijalankan dan mengembalikan pernyataan dalam isi pesan. Tugas tidak diselesaikan sampai metode mengembalikan true atau false. Ini menjamin bahwa seluruh metode dijalankan. Tugas dapat berisi beberapa pernyataan tunggu yang dilindungi oleh Tugas. Pola ini harus digunakan dengan objek ControlChannelTrigger saat StreamWebSocket atau MessageWebSocket digunakan sebagai transportasi. Untuk operasi yang mungkin membutuhkan waktu lama untuk diselesaikan (operasi baca asinkron biasa, misalnya), aplikasi harus menggunakan pola asinkron mentah yang dibahas sebelumnya.

Sampel berikut mendaftarkan ControlChannelTrigger untuk pemberitahuan push dan WebSocket tetap aktif di 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
}

Untuk informasi selengkapnya tentang menggunakan MessageWebSocket atau StreamWebSocket dengan ControlChannelTrigger, lihat sampel ControlChannelTrigger StreamWebSocket.

ControlChannelTrigger dengan HttpClient

Beberapa pertimbangan khusus berlaku saat menggunakan HttpClient dengan ControlChannelTrigger. Ada beberapa pola penggunaan khusus transportasi dan praktik terbaik yang harus diikuti saat menggunakan HttpClient dengan ControlChannelTrigger. Selain itu, pertimbangan ini memengaruhi cara permintaan untuk menerima paket di HttpClient ditangani.

CatatanHttpClient menggunakan SSL saat ini tidak didukung menggunakan fitur pemicu jaringan dan ControlChannelTrigger.   Pola penggunaan dan praktik terbaik berikut harus diikuti saat menggunakan HttpClient dengan ControlChannelTrigger:

  • Aplikasi mungkin perlu mengatur berbagai properti dan header pada objek HttpClient atau HttpClientHandler di namespace System.Net.Http sebelum mengirim permintaan ke URI tertentu.
  • Aplikasi mungkin perlu membuat permintaan awal untuk menguji dan mengatur transportasi dengan benar sebelum membuat transportasi HttpClient untuk digunakan dengan ControlChannelTrigger. Setelah aplikasi menentukan bahwa transportasi dapat disiapkan dengan benar, objek HttpClient dapat dikonfigurasi sebagai objek transportasi yang digunakan dengan objek ControlChannelTrigger. Proses ini dirancang mencegah beberapa skenario memutus koneksi yang dibuat melalui transportasi. Menggunakan SSL dengan sertifikat, aplikasi mungkin memerlukan dialog untuk ditampilkan untuk entri PIN atau jika ada beberapa sertifikat yang dapat dipilih. Autentikasi proksi dan autentikasi server mungkin diperlukan. Jika autentikasi proksi atau server kedaluwarsa, koneksi mungkin ditutup. Salah satu cara aplikasi dapat menangani masalah kedaluwarsa autentikasi ini adalah dengan mengatur timer. Ketika pengalihan HTTP diperlukan, tidak dijamin bahwa koneksi kedua dapat dibuat dengan andal. Permintaan pengujian awal akan memastikan bahwa aplikasi dapat menggunakan URL yang dialihkan terbaru sebelum menggunakan objek HttpClient sebagai transportasi dengan objek ControlChannelTrigger .

Tidak seperti transportasi jaringan lainnya, objek HttpClient tidak dapat langsung diteruskan ke metode UsingTransport objek ControlChannelTrigger . Sebagai gantinya, objek HttpRequestMessage harus dibangun secara khusus untuk digunakan dengan objek HttpClient dan ControlChannelTrigger. Objek HttpRequestMessage dibuat menggunakan metode RtcRequestFactory.Create . Objek HttpRequestMessage yang dibuat kemudian diteruskan ke metode UsingTransport .

Sampel berikut menunjukkan cara membuat objek HttpRequestMessage untuk digunakan dengan objek HttpClient dan 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;
    }
}

Beberapa pertimbangan khusus memengaruhi cara permintaan untuk mengirim permintaan HTTP di HttpClient untuk memulai menerima respons ditangani. Secara khusus, saat menggunakan HttpClient dengan ControlChannelTrigger, aplikasi Anda harus menggunakan Tugas untuk menangani pengiriman alih-alih model tunggu.

Menggunakan HttpClient, tidak ada sinkronisasi dengan metode IBackgroundTask.Run pada tugas latar belakang untuk ControlChannelTrigger dengan pengembalian panggilan balik penyelesaian penerimaan. Untuk alasan ini, aplikasi hanya dapat menggunakan teknik pemblokiran HttpResponseMessage dalam metode Jalankan dan menunggu hingga seluruh respons diterima.

Menggunakan HttpClient dengan ControlChannelTrigger terlihat berbeda dari transportasi StreamSocket, MessageWebSocket, atau StreamWebSocket . Panggilan balik terima HttpClient dikirimkan melalui Tugas ke aplikasi sejak kode HttpClient . Ini berarti bahwa tugas pemberitahuan push ControlChannelTrigger akan diaktifkan segera setelah data atau kesalahan dikirim ke aplikasi. Dalam sampel di bawah ini, kode menyimpan responseTask yang dikembalikan oleh metode HttpClient.SendAsync ke penyimpanan global yang akan diambil tugas pemberitahuan push dan proses sebaris.

Contoh berikut menunjukkan cara menangani permintaan pengiriman di HttpClient saat digunakan dengan 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);
}

Sampel berikut menunjukkan cara membaca respons yang diterima di HttpClient saat digunakan dengan 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;
}

Untuk informasi selengkapnya tentang menggunakan HttpClient dengan ControlChannelTrigger, lihat sampel ControlChannelTrigger HttpClient.

ControlChannelTrigger dengan IXMLHttpRequest2

Beberapa pertimbangan khusus berlaku saat menggunakan IXMLHTTPRequest2 dengan ControlChannelTrigger. Ada beberapa pola penggunaan khusus transportasi dan praktik terbaik yang harus diikuti saat menggunakan IXMLHTTPRequest2 dengan ControlChannelTrigger. Menggunakan ControlChannelTrigger tidak memengaruhi cara permintaan untuk mengirim atau menerima permintaan HTTP pada IXMLHTTPRequest2 ditangani.

Pola penggunaan dan praktik terbaik saat menggunakan IXMLHTTPRequest2 dengan ControlChannelTrigger

  • Objek IXMLHTTPRequest2 saat digunakan sebagai transportasi hanya memiliki masa pakai satu permintaan/respons. Ketika digunakan dengan objek ControlChannelTrigger, lebih mudah untuk membuat dan menyiapkan objek ControlChannelTrigger sekali dan kemudian memanggil metode UsingTransport berulang kali, setiap kali mengaitkan objek IXMLHTTPRequest2 baru. Aplikasi harus menghapus objek IXMLHTTPRequest2 sebelumnya sebelum menyediakan objek IXMLHTTPRequest2 baru untuk memastikan bahwa aplikasi tidak melebihi batas sumber daya yang dialokasikan.
  • Aplikasi mungkin perlu memanggil metode SetProperty dan SetRequestHeader untuk menyiapkan transportasi HTTP sebelum memanggil metode Kirim.
  • Aplikasi mungkin perlu membuat permintaan Kirim awal untuk menguji dan menyiapkan transportasi dengan benar sebelum membuat transportasi yang akan digunakan dengan ControlChannelTrigger. Setelah aplikasi menentukan bahwa transportasi disiapkan dengan benar, objek IXMLHTTPRequest2 dapat dikonfigurasi sebagai objek transportasi yang digunakan dengan ControlChannelTrigger. Proses ini dirancang mencegah beberapa skenario memutus koneksi yang dibuat melalui transportasi. Menggunakan SSL dengan sertifikat, aplikasi mungkin memerlukan dialog untuk ditampilkan untuk entri PIN atau jika ada beberapa sertifikat yang dapat dipilih. Autentikasi proksi dan autentikasi server mungkin diperlukan. Jika autentikasi proksi atau server kedaluwarsa, koneksi mungkin ditutup. Salah satu cara aplikasi dapat menangani masalah kedaluwarsa autentikasi ini adalah dengan mengatur timer. Ketika pengalihan HTTP diperlukan, tidak dijamin bahwa koneksi kedua dapat dibuat dengan andal. Permintaan pengujian awal akan memastikan bahwa aplikasi dapat menggunakan URL yang dialihkan terbaru sebelum menggunakan objek IXMLHTTPRequest2 sebagai transportasi dengan objek ControlChannelTrigger .

Untuk informasi selengkapnya tentang menggunakan IXMLHTTPRequest2 dengan ControlChannelTrigger, lihat sampel ControlChannelTrigger dengan IXMLHTTPRequest2.

API penting