Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Om du vill fortsätta nätverkskommunikationen medan den inte är i förgrunden kan appen använda bakgrundsaktiviteter och ett av dessa två alternativ.
- Socket mäklare. Om din app använder socketar för långsiktiga anslutningar kan den delegera ägarskapet för en socket till en system socket broker när den lämnar förgrunden. Mäklaren aktiverar sedan din app när trafiken anländer till socketen, överför ägarskapet tillbaka till din app, och din app bearbetar därefter den ankommande trafiken.
- Kontrollkanalutlösare
Att utföra nätverksåtgärder i bakgrundsprocesser
- Använd en SocketActivityTrigger- för att aktivera bakgrundsaktiviteten när ett paket tas emot och du måste utföra en kortvarig uppgift. När du har utfört uppgiften bör bakgrundsaktiviteten avslutas för att spara ström.
- Använd en ControlChannelTrigger- för att aktivera bakgrundsaktiviteten när ett paket tas emot och du måste utföra en långlivad uppgift.
Nätverksrelaterade villkor och flaggor
- Lägg till villkoret InternetAvailable i bakgrundsaktiviteten BackgroundTaskBuilder.AddCondition för att fördröja utlösandet av bakgrundsaktiviteten tills nätverksstacken körs. Det här villkoret sparar ström eftersom bakgrundsaktiviteten inte körs förrän nätverket är igång. Det här villkoret ger inte aktivering i realtid.
Oavsett vilken utlösare du använder ställer du in IsNetworkRequested på bakgrundsaktiviteten för att säkerställa att nätverket förblir uppe medan bakgrundsaktiviteten körs. Detta talar om för infrastrukturen för bakgrundsaktiviteten att hålla nätverket uppe medan aktiviteten körs, även om enheten har gått in i läget Anslutet vänteläge. Om bakgrundsaktiviteten inte använder IsNetworkRequestedkommer bakgrundsaktiviteten inte att kunna komma åt nätverket i läget Anslutet vänteläge (till exempel när en telefons skärm är avstängd).
Socket broker och SocketActivityTrigger
Om din app använder DatagramSocket, StreamSocketeller StreamSocketListener anslutningar bör du använda SocketActivityTrigger och socket broker för att meddelas när trafiken kommer till din app när den inte är i förgrunden.
För att appen ska kunna ta emot och bearbeta data som tas emot på en socket när appen inte är aktiv måste appen utföra en engångskonfiguration vid start och sedan överföra socketägarskapet till socket-koordinatorn när den övergår till ett tillstånd där den inte är aktiv.
Enstaka konfigurationssteg är att skapa en utlösare, registrera en bakgrundsaktivitet för utlösaren och aktivera socketen för socket-koordinatorn:
- Skapa en SocketActivityTrigger- och registrera en bakgrundsuppgift för utlösaren med parametern TaskEntryPoint inställd på din kod för bearbetning av ett mottaget paket.
var socketTaskBuilder = new BackgroundTaskBuilder();
socketTaskBuilder.Name = _backgroundTaskName;
socketTaskBuilder.TaskEntryPoint = _backgroundTaskEntryPoint;
var trigger = new SocketActivityTrigger();
socketTaskBuilder.SetTrigger(trigger);
_task = socketTaskBuilder.Register();
- Anropa EnableTransferOwnership på socketen innan du binder den.
_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");
När din socket har konfigurerats korrekt och din app är på väg att pausas, anropar du TransferOwnership på socketen för att överföra den till en socket-mäklare. Mäklaren övervakar socketen och startar din bakgrundsuppgift när data tas emot. Följande exempel innehåller ett verktyg TransferOwnership-funktion för att utföra överföringen för StreamSocketListener sockets. (Observera att de olika typerna av socketar har sina egna TransferOwnership- metoder, så du måste anropa den metod som är lämplig för socketen vars ägarskap du överför. Koden skulle förmodligen innehålla en överlagrad TransferOwnership- hjälpmetod med en implementering för varje sockettyp som du använder, så att koden i OnSuspending förblir lättläst.)
En app överför ägarskapet för en socket till en socket broker och skickar ID:t för bakgrundsaktiviteten med hjälp av en av de lämpliga följande metoderna:
- En av metoderna TransferOwnership på en DatagramSocket.
- En av metoderna TransferOwnership på en StreamSocket.
- En av metoderna TransferOwnership på en StreamSocketListener.
// 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();
}
I bakgrundsaktivitetens händelsehanterare:
- Hämta först en bakgrundsaktivitetsuppskjutning så att du kan hantera händelsen med asynkrona metoder.
var deferral = taskInstance.GetDeferral();
- Extrahera sedan SocketActivityTriggerDetails från händelseargumenten och hitta orsaken till att händelsen skapades:
var details = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
var socketInformation = details.SocketInformation;
switch (details.Reason)
- Om händelsen aktiverades på grund av socketaktivitet skapar du en DataReader på socketen, läser in läsaren asynkront och använder sedan data enligt appens design. Observera att du måste returnera ägarskapet för socketen tillbaka till socket-koordinatorn för att kunna meddelas om ytterligare socketaktivitet igen.
I följande exempel visas texten som tas emot på socketen i ett popup-meddelande.
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;
- Om händelsen utlöstes på grund av att en keep alive-timer upphörde att gälla, bör din kod skicka viss data över socketen för att hålla den vid liv och starta om keep alive-timern. Återigen är det viktigt att returnera ägarskapet för sockeln till socketmäklaren för att få ytterligare händelsemeddelanden.
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;
- Om händelsen uppstod på grund av att socketen stängdes återupprättar du socketen och ser till att du överför ägarskapet för den till socket-koordinatorn när du har skapat den nya socketen. I det här exemplet lagras värdnamnet och porten i lokala inställningar så att de kan användas för att upprätta en ny socketanslutning:
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;
- Glöm inte att slutföra uppskjutningen när du har bearbetat händelsemeddelandet:
deferral.Complete();
Ett fullständigt exempel som visar användningen av SocketActivityTrigger och socket broker finns i exempelexemplet SocketActivityStreamSocket. Initieringen av socketen utförs i Scenario1_Connect.xaml.cs och implementeringen av bakgrundsaktiviteten finns i SocketActivityTask.cs.
Du kommer förmodligen att märka att exempelanropen TransferOwnership så snart den skapar en ny socket eller hämtar en befintlig socket, i stället för att använda OnSuspending till och med hanteraren för att göra det enligt beskrivningen i det här avsnittet. Det beror på att exemplet fokuserar på att demonstrera SocketActivityTriggeroch att den inte använder socketen för någon annan aktivitet medan den körs. Din applikation är förmodligen mer komplex och bör använda OnSuspending för att avgöra när du ska anropa TransferOwnership.
Kontrollkanalutlösare
Kontrollera först att du använder kontrollkanalutlösare (CCT) på rätt sätt. Om du använder DatagramSocket, StreamSocketeller StreamSocketListener anslutningar rekommenderar vi att du använder SocketActivityTrigger. Du kan använda CCT:er för StreamSocket, men de använder fler resurser och kanske inte fungerar i läget Anslutet vänteläge.
Om du använder WebSockets, IXMLHTTPRequest2, System.Net.Http.HttpClienteller Windows.Web.Http.HttpClientmåste du använda ControlChannelTrigger.
ControlChannelTrigger med WebSockets
Viktigt!
Funktionen som beskrivs i det här avsnittet (ControlChannelTrigger med WebSockets) stöds i version 10.0.15063.0 av SDK och tidigare. Det stöds också i förhandsversioner av Windows 10 Insider Preview.
Vissa särskilda överväganden gäller när du använder MessageWebSocket eller StreamWebSocket med ControlChannelTrigger. Det finns vissa transportspecifika användningsmönster och metodtips som bör följas när du använder en MessageWebSocket- eller StreamWebSocket- med ControlChannelTrigger. Dessutom påverkar dessa överväganden hur begäranden om att ta emot paket på StreamWebSocket- hanteras. Begäranden om att ta emot paket på MessageWebSocket påverkas inte.
Följande användningsmönster och metodtips bör följas när du använder MessageWebSocket eller StreamWebSocket med ControlChannelTrigger:
- En utestående socket mottagning måste hållas bokförd hela tiden. Detta krävs för att push-meddelandeuppgifterna ska kunna utföras.
- WebSocket-protokollet definierar en standardmodell för keep-alive-meddelanden. Klassen WebSocketKeepAlive kan skicka klientinitierade WebSocket-protokoll keep-alive-meddelanden till servern. Appen ska registrera klassen WebSocketKeepAlive som uppgiftsstartpunkt för en KeepAliveTrigger.
Vissa särskilda överväganden påverkar hur begäranden om att ta emot paket på StreamWebSocket- hanteras. När du använder en StreamWebSocket- med ControlChannelTriggermåste appen använda ett råa asynkront mönster för att hantera läsningar i stället för invänta modell i C# och VB.NET eller Uppgifter i C++. Det råa asynkrona mönstret visas i ett kodexempel senare i det här avsnittet.
Med hjälp av det råa asynkrona mönstret kan Windows synkronisera metoden IBackgroundTask.Run för bakgrundsaktiviteten för ControlChannelTrigger- med returen av återanropet för mottagna slutförande. Metoden Kör anropas efter att återanropsfunktionen har slutförts. Detta säkerställer att appen har tagit emot data/fel innan metoden Kör anropas.
Det är viktigt att observera att appen måste utföra ytterligare en läsning innan den returnerar kontrollen från återanropet. Observera också att DataReader inte kan användas direkt med MessageWebSocket eller StreamWebSocket transport eftersom det bryter synkroniseringen som beskrivs ovan. Det går inte att använda metoden DataReader.LoadAsync direkt ovanpå transporten. I stället kan IBuffer- som returneras av IInputStream.ReadAsync-metoden på egenskapen StreamWebSocket.InputStream senare skickas till DataReader.FromBuffer-metoden för vidare bearbetning.
Följande exempel visar hur du använder ett rå asynkront mönster för att hantera läsningar på 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);
}
}
Läskompletteringshanteraren utlöses garanterat innan metoden IBackgroundTask.Run på bakgrundsuppgiften för ControlChannelTrigger anropas. Windows har en intern synkronisering för att vänta på att en app ska återkomma från ett slutförande avläsningsåteranrop. Appen bearbetar vanligtvis snabbt data eller felet från MessageWebSocket- eller StreamWebSocket- i återanropet för att slutföra läsningen. Själva meddelandet bearbetas i kontexten för metoden IBackgroundTask.Run. I det här exemplet nedan illustreras den här punkten med hjälp av en meddelandekö som läskompletteringshanteraren infogar meddelandet i och bakgrundsaktiviteten senare bearbetar.
Följande exempel visar läsavslutshanteraren som ska användas med ett rått asynkront mönster för hantering av läsoperationer på 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);
}
En ytterligare detalj med avseende på WebSockets är keep-alive-hanteraren. WebSocket-protokollet definierar en standardmodell för keep-alive-meddelanden.
När du använder MessageWebSocket eller StreamWebSocketregistrerar du en WebSocketKeepAlive- klassinstans som TaskEntryPoint för en KeepAliveTrigger så att appen kan tas bort och skicka keep-alive-meddelanden till servern (fjärrslutpunkt) med jämna mellanrum. Detta bör göras som en del av appkoden för bakgrundsregistrering samt i paketmanifestet.
Den här startpunkten för Windows.Sockets.WebSocketKeepAlive måste anges på två platser:
- När du skapar KeepAliveTrigger-utlösaren i källkoden (se exemplet nedan).
- I apppaketmanifestet för keepalive-bakgrundsaktivitetsdeklarationen.
Följande exempel lägger till ett meddelande om nätverksutlösare och en keepalive-utlösare under elementet <Application> i ett appmanifest.
<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>
En app måste vara mycket försiktig när den använder ett invänta-uttryck i samband med en ControlChannelTrigger- och en asynkron åtgärd på en StreamWebSocket, MessageWebSocketeller StreamSocket. Ett Task<bool> objekt kan användas för att registrera en ControlChannelTrigger för pushnotiser och WebSocket keep-alives på StreamWebSocket och för att koppla upp transporten. Som en del av registreringen anges StreamWebSocket som transport för ControlChannelTrigger och en läsning utförs. Task.Result kommer att blockera den aktuella tråden tills alla steg i uppgiften har körts och returnera satser i meddelandekroppen. Uppgiften är inte löst tills metoden returnerar antingen sant eller falskt. Detta garanterar att hela metoden körs. Uppgift kan innehålla flera vänta-instruktioner som skyddas av Uppgift. Det här mönstret ska användas med objektet ControlChannelTrigger när en StreamWebSocket- eller MessageWebSocket används som transport. För de åtgärder som kan ta lång tid att slutföra (till exempel en typisk asynkron läsåtgärd) bör appen använda det råa asynkrona mönster som beskrevs tidigare.
Följande exempel registrerar ControlChannelTrigger för push-meddelanden och WebSocket-keep-alives på 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
}
Mer information om hur du använder MessageWebSocket eller StreamWebSocket med ControlChannelTriggerfinns i ControlChannelTrigger StreamWebSocket-exempel.
ControlChannelTrigger tillsammans med HttpClient
Vissa särskilda överväganden gäller när du använder HttpClient- med ControlChannelTrigger. Det finns vissa transportspecifika användningsmönster och metodtips som bör följas när du använder en HttpClient- med ControlChannelTrigger. Dessutom påverkar dessa överväganden hur begäranden om att ta emot paket på HttpClient- hanteras.
NoteHttpClient using SSL stöds inte för närvarande med hjälp av funktionen för nätverksutlösare och ControlChannelTrigger. Följande användningsmönster och metodtips bör följas när du använder HttpClient- med ControlChannelTrigger:
- Appen kan behöva ange olika egenskaper och rubriker på HttpClient eller HttpClientHandler-objekt i System.Net.Http-namnområdet innan begäran skickas till den specifika URI:n.
- En app kan behöva göra en första begäran för att testa och konfigurera transporten korrekt innan du skapar HttpClient- transport som ska användas med ControlChannelTrigger. När appen har fastställt att transporten kan konfigureras korrekt kan ett HttpClient--objekt konfigureras som transportobjektet som används med objektet ControlChannelTrigger. Den här processen är utformad för att förhindra att vissa scenarier bryter anslutningen som upprättas via transporten. Med hjälp av SSL med ett certifikat kan en app kräva att en dialogruta visas för PIN-kodsinmatning eller om det finns flera certifikat att välja mellan. Proxyautentisering och serverautentisering kan krävas. Om proxy- eller serverautentiseringen upphör att gälla kan anslutningen stängas. Ett sätt för en app att hantera dessa problem med förfallodatum för autentisering är att ange en timer. När en HTTP-omdirigering krävs garanteras det inte att den andra anslutningen kan upprättas på ett tillförlitligt sätt. En inledande testbegäran säkerställer att appen kan använda den senaste omdirigerade URL:en up-toinnan HttpClient--objektet används för transport tillsammans med ControlChannelTrigger--objektet.
Till skillnad från andra nätverkstransporter kan HttpClient--objektet inte skickas direkt till UsingTransport-metoden för ControlChannelTrigger-objektet. I stället måste ett HttpRequestMessage--objekt vara särskilt konstruerat för användning med objektet HttpClient och ControlChannelTrigger. Objektet HttpRequestMessage skapas med hjälp av metoden RtcRequestFactory.Create. Objektet HttpRequestMessage som skapas skickas sedan till metoden UsingTransport.
Följande exempel visar hur du skapar ett HttpRequestMessage--objekt för användning med objektet HttpClient och 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;
}
}
Vissa särskilda överväganden påverkar hur begäranden om att skicka HTTP-begäranden på HttpClient- för att initiera mottagandet av ett svar hanteras. När du använder en HttpClient- med ControlChannelTriggermåste appen använda en uppgift för att hantera sändningar i stället för väntemodellen.
Med hjälp av HttpClientfinns det ingen synkronisering med metoden IBackgroundTask.Run för bakgrundsprocessen för ControlChannelTrigger- med återkomsten av slutförda återanrop. Därför kan appen bara använda den blockerande HttpResponseMessage-tekniken i metoden Kör och vänta tills hela svaret tas emot.
Att använda HttpClient med ControlChannelTrigger skiljer sig märkbart från StreamSocket, MessageWebSocket eller StreamWebSocket transporterna. HttpClient- motringning levereras via en aktivitet till appen sedan koden HttpClient. Det innebär att ControlChannelTrigger- push-meddelandeaktiviteten utlöses så snart data eller fel skickas till appen. I exemplet nedan lagrar koden responseTask som returneras av metoden HttpClient.SendAsync i global lagring som push-anmälningsuppgiften hämtar och bearbetar i linje.
Följande exempel visar hur du hanterar skickaförfrågningar med HttpClient- när den används med 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);
}
Följande exempel visar hur du läser svar som tas emot på HttpClient- när de används med 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;
}
Mer information om hur du använder HttpClient med ControlChannelTriggerfinns i ControlChannelTrigger HttpClient-exempel.
ControlChannelTrigger med IXMLHttpRequest2
Vissa särskilda överväganden gäller när du använder IXMLHTTPRequest2 med ControlChannelTrigger. Det finns vissa transportspecifika användningsmönster och metodtips som bör följas när du använder en IXMLHTTPRequest2 med ControlChannelTrigger. Att använda ControlChannelTrigger påverkar inte hur begäranden om att skicka eller ta emot HTTP-begäranden på IXMLHTTPRequest2- hanteras.
Användningsmönster och metodtips när du använder IXMLHTTPRequest2 med ControlChannelTrigger
- Ett IXMLHTTPRequest2--objekt när det används som transport har en livslängd på endast en begäran/ett svar. När det används med objektet ControlChannelTrigger är det praktiskt att skapa och konfigurera ControlChannelTrigger-objektet en gång och sedan anropa metoden UsingTransport upprepade gånger, varje gång associera ett nytt IXMLHTTPRequest2 objekt. En app bör ta bort föregående IXMLHTTPRequest2- innan du anger ett nytt IXMLHTTPRequest2- objekt för att säkerställa att appen inte överskrider de allokerade resursgränserna.
- Appen kan behöva anropa metoderna SetProperty och SetRequestHeader för att konfigurera HTTP-transporten innan Send-metoden anropas.
- En app kan behöva göra en första Skicka begäran för att testa och konfigurera transporten på rätt sätt innan transporten skapas för användning med ControlChannelTrigger. När appen har fastställt att transporten är korrekt konfigurerad kan IXMLHTTPRequest2-objektet konfigureras som transportobjektet som används med ControlChannelTrigger. Den här processen är utformad för att förhindra att vissa scenarier bryter anslutningen som upprättas via transporten. Med hjälp av SSL med ett certifikat kan en app kräva att en dialogruta visas för PIN-kodsinmatning eller om det finns flera certifikat att välja mellan. Proxyautentisering och serverautentisering kan krävas. Om proxy- eller serverautentiseringen upphör att gälla kan anslutningen stängas. Ett sätt för en app att hantera dessa problem med förfallodatum för autentisering är att ange en timer. När en HTTP-omdirigering krävs garanteras det inte att den andra anslutningen kan upprättas på ett tillförlitligt sätt. En första testbegäran säkerställer att appen kan använda den mest up-to-datumomdirigerade URL:en innan IXMLHTTPRequest2-objektet som transport med ControlChannelTrigger-objektet.
Mer information om hur du använder IXMLHTTPRequest2 med ControlChannelTriggerfinns i ControlChannelTrigger med IXMLHTTPRequest2-exempel.