Hálózati kommunikáció a háttérben

Ha folytatni szeretné a hálózati kommunikációt, miközben nincs az előtérben, az alkalmazás háttérfeladatokat és a két lehetőség egyikét használhatja.

  • Szoftvercsatorna-közvetítő. Ha az alkalmazás szoftvercsatornákat használ hosszú távú kapcsolatokhoz, akkor amikor elhagyja az előteret, a szoftvercsatornák tulajdonjogát egy rendszercsatorna-közvetítőnek delegálhatja. A közvetítő ezután: aktiválja az alkalmazást, amikor a forgalom megérkezik a szoftvercsatornára; visszaviszi a tulajdonjogot az alkalmazásnak; és az alkalmazás ezután feldolgozza az érkező forgalmat.
  • Csatorna eseményindítóinak vezérlése.

Hálózati műveletek végrehajtása háttérfeladatokban

  • A SocketActivityTrigger használatával aktiválhatja a háttérfeladatot egy csomag érkezésekor, és rövid élettartamú feladatot kell végrehajtania. A feladat végrehajtása után a háttérfeladatnak le kell fejeződnie az energiamegtakarítás érdekében.
  • A ControlChannelTriggerrel aktiválhatja a háttérfeladatot egy csomag érkezésekor, és hosszú élettartamú feladatot kell végrehajtania.

Hálózattal kapcsolatos feltételek és jelzők

  • Adja hozzá a InternetAvailable feltételt a háttérfeladathoz BackgroundTaskBuilder.AddCondition a háttérfeladat aktiválásának késleltetéséhez, amíg a hálózati verem nem fut. Ez a feltétel energiát takarít meg, mert a háttérfeladat nem lesz végrehajtva, amíg a hálózat be nem áll. Ez a feltétel nem biztosít valós idejű aktiválást.

A használt eseményindítótól függetlenül állítsa be az IsNetworkRequested parancsot a háttérfeladatra, hogy a hálózat a háttérfeladat futtatása közben is megmaradjon. Ez azt jelzi a háttérfeladat-infrastruktúrának, hogy a feladat végrehajtása közben is tartsa fenn a hálózatot, még akkor is, ha az eszköz csatlakoztatott készenléti üzemmódba lépett. Ha a háttérfeladat nem használja az IsNetworkRequested szolgáltatást, akkor a háttérfeladat nem fogja tudni elérni a hálózatot csatlakoztatott készenléti módban (például ha a telefon képernyője ki van kapcsolva).

A socketközvetítő és a SocketActivityTrigger

Ha az alkalmazás DatagramSocket, StreamSocket vagy StreamSocketListener kapcsolatokat használ, akkor a SocketActivityTrigger és a szoftvercsatorna-közvetítő használatával értesítést kaphat arról, hogy mikor érkezik forgalom az alkalmazáshoz, miközben az nem az előtérben van.

Ahhoz, hogy az alkalmazás megkapja és feldolgozhassa a szoftvercsatornán kapott adatokat, amikor az alkalmazás nem aktív, az alkalmazásnak egyszeri beállítást kell végrehajtania indításkor, majd át kell adnia a szoftvercsatornák tulajdonjogát a szoftvercsatorna-közvetítőnek, amikor olyan állapotba kerül, ahol nem aktív.

Az egyszeri beállításhoz a következő lépések szükségesek: eseményindító létrehozása, háttérfeladat regisztrálása az eseményindítóhoz, valamint a socket engedélyezése a socketközvetítő számára:

  • Hozzon létre egy SocketActivityTrigger elemet, és regisztráljon egy háttérfeladatot ehhez a triggerhez úgy, hogy a TaskEntryPoint paraméterét a fogadott csomag feldolgozását végző saját kódjára állítja.
            var socketTaskBuilder = new BackgroundTaskBuilder();
            socketTaskBuilder.Name = _backgroundTaskName;
            socketTaskBuilder.TaskEntryPoint = _backgroundTaskEntryPoint;
            var trigger = new SocketActivityTrigger();
            socketTaskBuilder.SetTrigger(trigger);
            _task = socketTaskBuilder.Register();
  • A socket kötése előtt hívja meg a socketen az EnableTransferOwnership metódust.
           _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");

Miután a socket megfelelően be van állítva, amikor az alkalmazás felfüggesztésre készül, hívja meg a socketon a TransferOwnership függvényt, hogy átadja azt egy socketközvetítőnek. A közvetítőprogram figyeli a socketet, és amikor adat érkezik, aktiválja a háttérfeladatát. Az alábbi példa egy TransferOwnership segédfüggvényt tartalmaz a StreamSocketListener foglalatokhoz tartozó átvitel végrehajtásához. (Vegye figyelembe, hogy a különböző típusú szoftvercsatornák saját TransferOwnership metódussal rendelkeznek, ezért meg kell hívnia annak a szoftvercsatornának megfelelő metódust, amelynek a tulajdonjogát átviszi. A kód valószínűleg tartalmazna egy túlterhelt TransferOwnership-segédet minden egyes használt szoftvercsatornához, így az OnSuspending kód könnyen olvasható marad.)

Egy alkalmazás átadja egy foglalat tulajdonjogát egy foglalatközvetítőnek, és a háttérfeladathoz tartozó azonosítót az alábbi módszerek közül a megfelelőt használva adja át:


// declare int _transferOwnershipCount as a field.

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

A háttérfeladat eseménykezelőjében:

  • Először szerezze be a háttérfeladat-halasztást, hogy aszinkron metódusokkal kezelje az eseményt.
var deferral = taskInstance.GetDeferral();
  • Ezután nyerje ki a SocketActivityTriggerDetails függvényt az eseményargumentumokból, és keresse meg az esemény létrehozásának okát:
var details = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
    var socketInformation = details.SocketInformation;
    switch (details.Reason)
  • Ha az esemény sockettevékenység miatt váltódott ki, hozzon létre egy DataReader objektumot a sockethez, töltse be azt aszinkron módon, majd használja fel az adatokat az alkalmazás kialakításának megfelelően. Vegye figyelembe, hogy vissza kell adnia a socket feletti rendelkezési jogot a socket brokernek ahhoz, hogy ismét értesítést kapjon a socket további aktivitásáról.

Az alábbi példában a szoftvercsatornán kapott szöveg egy bejelentésben jelenik meg.

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;
  • Ha az eseményt az váltotta ki, hogy lejárt a keep-alive időzítő, akkor a kódnak adatot kell küldenie a socketen keresztül, hogy életben tartsa a socketet, és újraindítsa a keep-alive időzítőt. Ismét fontos, hogy a socket tulajdonjogát visszaadjuk a socketközvetítőnek, hogy további eseményértesítéseket kapjunk:
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;
  • Ha az esemény azért lett felemelve, mert a szoftvercsatornát bezárták, hozza létre újra a szoftvercsatornát, és győződjön meg arról, hogy az új szoftvercsatorna létrehozása után a szoftvercsatorna-közvetítőnek átadja annak tulajdonjogát. Ebben a mintában a gazdagépnév és a port a helyi beállításokban vannak tárolva, hogy felhasználhatók legyenek egy új aljzatkapcsolat létrehozására:
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;
  • Ne felejtse el befejezni a halasztást, miután befejezte az eseményértesítés feldolgozását:
  deferral.Complete();

A SocketActivityTrigger és a foglalatközvetítő használatát bemutató teljes mintáért lásd a SocketActivityStreamSocket mintát. A socket inicializálása a Scenario1_Connect.xaml.cs fájlban történik, a háttérfeladat megvalósítása pedig a SocketActivityTask.cs fájlban található.

Valószínűleg észre fogja venni, hogy a minta a TransferOwnership függvényt hívja meg, amint létrehoz egy új foglalatot vagy megszerez egy meglévő foglalatot, nem pedig az OnSuspending eseménykezelőt használja erre, ahogyan az ebben a témakörben le van írva. Ennek az az oka, hogy a minta a SocketActivityTrigger bemutatására összpontosít, és futás közben nem használja a szoftvercsatornát semmilyen más tevékenységhez. Az alkalmazás valószínűleg összetettebb lesz, és az OnSuspending használatával kell meghatároznia, hogy mikor hívja meg a TransferOwnership-t.

Csatorna eseményindítóinak vezérlése

Először győződjön meg arról, hogy a vezérlőcsatorna-eseményindítókat (CCT-ket) megfelelően használja. Ha DatagramSocket, StreamSocket vagy StreamSocketListener kapcsolatokat használ, javasoljuk a SocketActivityTrigger használatát. CcT-eket használhat a StreamSockethez, de több erőforrást használnak, és előfordulhat, hogy nem működnek csatlakoztatott készenléti módban.

WebSockets, IXMLHTTPRequest2, System.Net.Http.HttpClient vagy Windows használata esetén. Web.Http.HttpClient, akkor a ControlChannelTriggert kell használnia.

ControlChannelTrigger WebSocketekkel

Important

Az ebben a szakaszban ismertetett funkció (a ControlChannelTrigger és a WebSockets) Windows SDK 10.0.15063.0-s és újabb verzióiban támogatott.

A MessageWebSocket vagy a StreamWebSocketControlChannelTriggerrel való használatakor bizonyos speciális szempontok érvényesek. A ControlChannelTriggerrel a MessageWebSocket vagy a StreamWebSocket használatakor néhány átviteli specifikus használati mintát és ajánlott eljárást kell követni. Emellett ezek a megfontolások befolyásolják, hogy a csomagok fogadására irányuló kéréseket hogyan kezelik a StreamWebSocket esetében. A MessageWebSocket csomagjainak fogadására irányuló kérelmekre nincs hatással.

A MessageWebSocket vagy a StreamWebSocketControlChannelTriggerrel való használatakor a következő használati mintákat és ajánlott eljárásokat kell követni:

  • A ki nem egyenlített szoftvercsatornák fogadását mindig közzé kell tenni. Ez a leküldéses értesítési feladatok végrehajtásának engedélyezéséhez szükséges.
  • A WebSocket protokoll egy szabványos modellt határoz meg az életben maradó üzenetekhez. A WebSocketKeepAlive osztály képes az ügyfél által kezdeményezett WebSocket protokoll életben tartására vonatkozó üzeneteket küldeni a kiszolgálónak. A WebSocketKeepAlive osztályt az alkalmazásnak a KeepAliveTrigger TaskEntryPointjaként kell regisztrálnia.

Egyes speciális szempontok befolyásolják a StreamWebSocketen lévő csomagok fogadására irányuló kérések kezelését. Különösen akkor, ha StreamWebSocketet használ a ControlChannelTriggerrel, az alkalmazásnak nyers aszinkron mintát kell használnia az olvasások kezeléséhez a várt modell helyett C# és VB.NET vagy C++-feladatok esetén. A nyers aszinkron mintát a szakasz későbbi részében egy kódmintában szemlélteti.

A nyers aszinkron minta használatával Windows szinkronizálhatja az IBackgroundTask.Run metódust a ControlChannelTrigger háttérfeladatán a fogadási befejezési visszahívás visszatérésével. A Run metódus a befejezési visszahívás visszatérése után kerül meghívásra. Ez biztosítja, hogy az alkalmazás megkapja az adatokat/hibákat a Futtatás metódus meghívása előtt.

Fontos megjegyezni, hogy az alkalmazásnak újabb olvasási kérést kell indítania, mielőtt a befejezési callbackből visszaadja a vezérlést. Azt is fontos megjegyezni, hogy a DataReader nem használható közvetlenül a MessageWebSocket vagy a StreamWebSocket átvitelhez, mivel ez megszakítja a fent leírt szinkronizálást. A DataReader.LoadAsync metódus használata nem támogatott közvetlenül az átvitel tetején. Ehelyett a StreamWebSocket.InputStream tulajdonság IInputStream.ReadAsync metódusa által visszaadott IBuffer-metódus később továbbítható a DataReader.FromBuffer metódusnak további feldolgozás céljából.

Az alábbi minta bemutatja, hogyan használható nyers aszinkron minta a StreamWebSocket olvasásainak kezelésére.

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

Az olvasási befejezési kezelő garantáltan aktiválódik, mielőtt az IBackgroundTask.Run metódust meghívja a ControlChannelTrigger háttérfeladatán. A Windows belső szinkronizációt használ, hogy megvárja, amíg az alkalmazás vissza nem tér az olvasás befejezését jelző visszahívásból. Az alkalmazás általában gyorsan feldolgozza a MessageWebSocket vagy a StreamWebSocket adatait vagy hibáját az olvasási befejezés visszahívásában. Maga az üzenet az IBackgroundTask.Run metódus kontextusában lesz feldolgozva. Az alábbi példa ezt egy olyan üzenetsor használatával szemlélteti, amelybe az olvasási befejezési kezelő elhelyezi az üzenetet, a háttérfeladat pedig később feldolgozza azt.

Az alábbi minta az olvasási befejező kezelőt mutatja be, amelyet nyers aszinkron mintával használ a StreamWebSocket olvasásainak kezelésére.

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

A Websockets további részlete a keep-alive kezelő. A WebSocket protokoll egy szabványos modellt határoz meg az életben maradó üzenetekhez.

A MessageWebSocket vagy StreamWebSocket használatakor regisztráljon egy WebSocketKeepAlive osztálypéldányt egy KeepAliveTrigger TaskEntryPoint bejegyzéseként, hogy az alkalmazás kivehető legyen a felfüggesztett állapotból, és rendszeresen keep-alive üzeneteket küldhessen a kiszolgálónak (távoli végpontnak). Ezt a háttérregisztrációs alkalmazáskód és a csomagjegyzék részeként kell elvégezni.

Ennek a feladatnak a Windows.Sockets.WebSocketKeepAlive belépési pontját két helyen kell megadni:

  • KeepAliveTrigger-eseményindító létrehozásakor a forráskódban (lásd az alábbi példát).
  • Az alkalmazáscsomag jegyzékfájljában, a keepalive háttérfeladat deklarációjához.

Az alábbi minta egy hálózati eseményindító-értesítést és egy életben tartási eseményindítót ad hozzá az alkalmazásjegyzék <Application> eleme alá.

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

Az alkalmazásoknak rendkívül óvatosnak kell lenniük , ha várakozási utasítást használnak egy ControlChannelTrigger környezetében, valamint egy StreamWebSocket, MessageWebSocket vagy StreamSocket aszinkron műveletében. A Task<bool> objektum használható egy ControlChannelTrigger regisztrálására push értesítésekhez és a WebSocket-kapcsolat életben tartásához a StreamWebSocket objektumon, valamint az átvitel csatlakoztatására. A regisztráció részeként a StreamWebSocket a ControlChannelTrigger átviteli mechanizmusaként van beállítva, és egy olvasási művelet kerül kiadásra. A Task.Result addig blokkolja az aktuális szálat, amíg a tevékenység összes lépése nem hajtja végre és adja vissza az üzenettörzsben lévő utasításokat. A feladat nem oldódik fel, amíg a metódus nem ad vissza igaz vagy hamis értéket. Ez garantálja a teljes metódus végrehajtását. A tevékenység több várakozási utasítást is tartalmazhat, amelyeket a tevékenység véd. Ezt a mintát a ControlChannelTrigger objektummal kell használni, ha streamWebSocket vagy MessageWebSocket van használva átvitelként. Azoknál a műveleteknél, amelyek végrehajtása hosszú ideig tarthat (például egy tipikus aszinkron olvasási művelet), az alkalmazásnak a korábban tárgyalt nyers aszinkron mintát kell használnia.

Az alábbi minta regisztrálja a ControlChannelTriggert a leküldéses értesítésekhez és a WebSocket életben tartásához a StreamWebSocketen.

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

A MessageWebSocket vagy a StreamWebSocketControlChannelTriggerrel való használatáról további információt a ControlChannelTrigger StreamWebSocket mintájában talál.

ControlChannelTrigger és HttpClient

A HttpClientControlChannelTriggerrel való használatakor bizonyos speciális szempontokat kell figyelembe venni. A ControlChannelTriggerrel rendelkező HttpClient használatakor néhány átviteli specifikus használati mintát és ajánlott eljárást kell követni. Ezen túlmenően ezek a szempontok befolyásolják a csomagok HttpClienten való fogadására irányuló kérések kezelését.

Megjegyzés: Az SSL-t használó HttpClient jelenleg nem támogatott a hálózati eseményindító funkció és a ControlChannelTrigger használatával.   A HttpClientControlChannelTriggerrel való használatakor a következő használati mintákat és ajánlott eljárásokat kell követni:

  • Előfordulhat, hogy az alkalmazásnak különböző tulajdonságokat és fejléceket kell beállítania a System.Net.Http névtér HttpClient vagy HttpClientHandler objektumán, mielőtt elküldené a kérést az adott URI-nak.
  • Előfordulhat, hogy egy alkalmazásnak kezdeti kérésre van szüksége az átvitel megfelelő teszteléséhez és beállításához, mielőtt létrehozza a ControlChannelTriggerrel használni kívánt HttpClient-átvitelt. Miután az alkalmazás megállapította, hogy az átvitel megfelelően beállítható, egy HttpClient-objektum konfigurálható a ControlChannelTrigger objektummal használt átviteli objektumként. Ez a folyamat megakadályozza, hogy egyes forgatókönyvek megszakítják a szállításon keresztül létrehozott kapcsolatot. Ha SSL-t használ egy tanúsítvánnyal, előfordulhat, hogy egy alkalmazásnak meg kell jelennie egy párbeszédpanelt a PIN-kód megadásához, vagy ha több tanúsítvány közül kell választania. Proxyhitelesítésre és kiszolgálóhitelesítésre lehet szükség. Ha a proxy- vagy kiszolgálóhitelesítés lejár, a kapcsolat megszakadhat. Az alkalmazások egy időzítő beállításával kezelhetik ezeket a hitelesítési lejárati problémákat. HA HTTP-átirányításra van szükség, nem garantált, hogy a második kapcsolat megbízhatóan létrejön. Egy kezdeti tesztkérés gondoskodik arról, hogy az alkalmazás a legfrissebb átirányított URL-címet használhassa, mielőtt a HttpClient objektumot átviteli mechanizmusként használná a ControlChannelTrigger objektummal.

A többi hálózati átviteltől eltérően a HttpClient objektum nem továbbítható közvetlenül a ControlChannelTrigger objektum UsingTransport metódusába. Ehelyett egy HttpRequestMessage objektumot kifejezetten a HttpClient objektumhoz és a ControlChannelTriggerhez való használatra kell kialakítani. A HttpRequestMessage objektum az RtcRequestFactory.Create metódussal jön létre. A létrehozott HttpRequestMessage objektum ezután a UsingTransport metódusnak lesz átadva.

Az alábbi minta bemutatja, hogyan hozhat létre HttpRequestMessage objektumot a HttpClient objektummal és a ControlChannelTriggerrel való használatra.

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

Egyes speciális szempontok befolyásolják, hogy hogyan kezelik a HTTP-kérések HttpClienten való küldését a válasz fogadásának kezdeményezésére. Különösen akkor, ha HttpClient-et használ a ControlChannelTriggerrel, az alkalmazásnak a várakozási modell helyett egy feladatot kell használnia a küldések kezeléséhez.

A HttpClient használatakor nincs szinkronizáció a ControlChannelTrigger háttérfeladatában futó IBackgroundTask.Run metódus és a fogadás befejezését jelző visszahívás visszatérése között. Ezért az alkalmazás a blokkoló HttpResponseMessage-technikát csak a Run metódusban használhatja, és meg kell várnia, amíg a teljes válasz megérkezik.

A HttpClientés a ControlChannelTrigger használata észrevehetően eltér a StreamSocket, a MessageWebSocket vagy a StreamWebSocket átvitelétől. A HttpClient fogadási visszahívása egy Taskon keresztül jut el az alkalmazáshoz, mivel a HttpClient kódja így működik. Ez azt jelenti, hogy a ControlChannelTrigger leküldéses értesítési feladat azonnal elindul, amint az adatokat vagy a hibát elküldi az alkalmazásnak. Az alábbi mintában a kód a HttpClient.SendAsync metódus által visszaadott responseTask fájlt globális tárolóba tárolja, amelyet a leküldéses értesítési feladat felvesz, és beágyazottan dolgoz fel.

Az alábbi minta bemutatja, hogyan kezelhetők a HttpClienten küldött kérelmek a ControlChannelTriggerrel való használatakor.

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

Az alábbi minta bemutatja, hogyan olvashatja el a HttpClienten kapott válaszokat a ControlChannelTrigger használatakor.

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

A HttpClientControlChannelTriggerrel való használatáról további információt a ControlChannelTrigger HttpClient-mintában talál.

ControlChannelTrigger az IXMLHttpRequest2 használatával

Az IXMLHTTPRequest2és a ControlChannelTrigger használatakor néhány speciális szempontot is figyelembe kell venni. Az IXMLHTTPRequest2és a ControlChannelTrigger használata esetén néhány átviteli specifikus használati mintát és ajánlott eljárást kell követni. A ControlChannelTrigger használata nem befolyásolja a HTTP-kérések IXMLHTTPRequest2-en való küldésének vagy fogadásának módját.

Használati minták és ajánlott eljárások az IXMLHTTPRequest2és a ControlChannelTrigger használatakor

  • Az átvitelként használt IXMLHTTPRequest2 objektum élettartama csak egy kérelem/válasz lehet. A ControlChannelTrigger objektummal való használat esetén célszerű egyszer létrehozni és beállítani a ControlChannelTrigger objektumot, majd meghívni a UsingTransport metódust, minden alkalommal, amikor új IXMLHTTPRequest2 objektumot társít. Az alkalmazásnak törölnie kell az előző IXMLHTTPRequest2 objektumot, mielőtt új IXMLHTTPRequest2 objektumot ad meg, hogy az alkalmazás ne lépje túl a lefoglalt erőforráskorlátokat.
  • Előfordulhat, hogy az alkalmazásnak meg kell hívnia a SetProperty és a SetRequestHeader metódust a HTTP-átvitel beállításához a Küldés metódus meghívása előtt.
  • Előfordulhat, hogy egy alkalmazásnak először el kell küldenie egy küldési kérést az átvitel megfelelő teszteléséhez és beállításához, mielőtt létrehozza a ControlChannelTriggerrel használni kívánt átvitelt. Miután az alkalmazás megállapította, hogy az átvitel megfelelően van beállítva, az IXMLHTTPRequest2 objektum konfigurálható a ControlChannelTriggerrel használt átviteli objektumként. Ez a folyamat megakadályozza, hogy egyes forgatókönyvek megszakítják a szállításon keresztül létrehozott kapcsolatot. Ha SSL-t használ egy tanúsítvánnyal, előfordulhat, hogy egy alkalmazásnak meg kell jelennie egy párbeszédpanelt a PIN-kód megadásához, vagy ha több tanúsítvány közül kell választania. Proxyhitelesítésre és kiszolgálóhitelesítésre lehet szükség. Ha a proxy- vagy kiszolgálóhitelesítés lejár, a kapcsolat megszakadhat. Az alkalmazások egy időzítő beállításával kezelhetik ezeket a hitelesítési lejárati problémákat. HA HTTP-átirányításra van szükség, nem garantált, hogy a második kapcsolat megbízhatóan létrejön. Egy kezdeti tesztkérés biztosítja, hogy az alkalmazás a legfrissebb átirányított URL-címet használhassa, mielőtt az IXMLHTTPRequest2 objektumot átviteli mechanizmusként használná a ControlChannelTrigger objektummal.

Az IXMLHTTPRequest2és a ControlChannelTrigger használatával kapcsolatos további információkért tekintse meg a ControlChannelTrigger és az IXMLHTTPRequest2 mintát.

Fontos API-k