Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Pokud chcete pokračovat v síťové komunikaci, i když není v popředí, může vaše aplikace používat úlohy na pozadí a jednu z těchto dvou možností.
- Zprostředkovatel soketů. Pokud vaše aplikace používá sokety pro dlouhodobá připojení, pak když opustí popředí, může delegovat vlastnictví soketu na systémového zprostředkovatele soketů. Zprostředkovatel pak: aktivuje vaši aplikaci při příchodu provozu do soketu; přenese vlastnictví zpět do vaší aplikace; a vaše aplikace pak zpracuje příchozí provoz.
- Aktivační události řídicího kanálu.
Provádění síťových operací v úlohách na pozadí
- Pomocí SocketActivityTrigger aktivujte úkol na pozadí při přijetí paketu, když potřebujete provést krátkodobý úkol. Po provedení úkolu by se úloha na pozadí měla ukončit, aby se ušetřila energie.
- Pomocí ControlChannelTrigger aktivujte úlohu na pozadí při přijetí paketu, když potřebujete provést dlouho trvající úlohu.
Podmínky a příznaky související se sítí
- Přidejte podmínku InternetAvailable do úlohy na pozadí pomocí BackgroundTaskBuilder.AddCondition k zpoždění spuštění úlohy na pozadí, dokud není spuštěn síťový zásobník. Tato podmínka šetří energii, protože úloha na pozadí se nespustí, dokud se síť nespustí. Tato podmínka neposkytuje aktivaci v reálném čase.
Bez ohledu na to, jaký spouštěč používáte, nastavte IsNetworkRequested na úlohu na pozadí, abyste zajistili, že síť zůstane aktivní, zatímco úloha na pozadí běží. To říká infrastruktuře úloh na pozadí, aby síť zůstala aktivní, zatímco se úloha provádí, i když zařízení přešlo do režimu připojení v pohotovostním režimu. Pokud úloha na pozadí nepoužívá IsNetworkRequested, nebude mít úkol na pozadí přístup k síti, když je v pohotovostním režimu připojeného telefonu (například když je vypnutá obrazovka telefonu).
Zprostředkovatel socketů a SocketActivityTrigger
Pokud vaše aplikace používá připojení DatagramSocket, StreamSocket nebo StreamSocketListener, měli byste použít SocketActivityTrigger a zprostředkovatele socketů, abyste byli upozorněni, když pro vaši aplikaci dorazí síťový provoz, zatímco není v popředí.
Aby mohla aplikace přijímat a zpracovávat data přijatá v soketu, když není aplikace aktivní, musí aplikace provést jednorázové nastavení při spuštění a potom přenést vlastnictví soketu do zprostředkovatele soketů, když přechází do stavu, ve kterém není aktivní.
Jednorázové kroky nastavení jsou vytvoření aktivační události, registrace úlohy na pozadí pro tuto aktivační událost a povolení soketu pro zprostředkovatele soketů:
- Vytvořte SocketActivityTrigger a zaregistrujte úlohu na pozadí pro trigger s parametrem TaskEntryPoint nastaveným na váš kód pro zpracování přijatého paketu.
var socketTaskBuilder = new BackgroundTaskBuilder();
socketTaskBuilder.Name = _backgroundTaskName;
socketTaskBuilder.TaskEntryPoint = _backgroundTaskEntryPoint;
var trigger = new SocketActivityTrigger();
socketTaskBuilder.SetTrigger(trigger);
_task = socketTaskBuilder.Register();
- Předtím, než soket navážete, zavolejte u něj EnableTransferOwnership.
_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");
Jakmile je soket správně nastavený, když se vaše aplikace chystá pozastavit, zavolejte transferOwnership na soketu a přepošlete ho na zprostředkovatele soketu. Broker sleduje socket a spustí vaši úlohu na pozadí, když jsou přijata data. Následující příklad obsahuje pomocnou funkci TransferOwnership pro převod soketů StreamSocketListener. (Všimněte si, že různé typy soketů mají vlastní transferOwnership metoda, takže musíte volat metodu odpovídající soketu, jehož vlastnictví převádíte. Váš kód by pravděpodobně obsahoval přetíženou pomocnou rutinu TransferOwnership s jednou implementací pro každý typ soketu, který používáte, aby kód OnSuspending zůstal snadno čitelný.)
Aplikace přenese vlastnictví soketu do zprostředkovatele soketu a předá ID úlohy na pozadí pomocí odpovídající jedné z následujících metod:
- Jedna z metod TransferOwnership v DatagramSocket.
- Jedna z metod TransferOwnership na StreamSocket.
- Jedna z metod TransferOwnership na StreamSocketListener.
// 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();
}
V obsluze události úlohy na pozadí:
- Nejprve získejte odložení úlohy na pozadí, abyste mohli zpracovat událost pomocí asynchronních metod.
var deferral = taskInstance.GetDeferral();
- Dále extrahujte SocketActivityTriggerDetails z argumentů události a vyhledejte důvod, proč byla událost vyvolána:
var details = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
var socketInformation = details.SocketInformation;
switch (details.Reason)
- Pokud byla událost vyvolána kvůli aktivitě soketu, vytvořte pro soket objekt DataReader, asynchronně načtěte data do čtečky a potom data použijte podle návrhu vaší aplikace. Mějte na paměti, že musíte vrátit vlastnictví soketu zpět správci soketů, abyste byli znovu upozorněni na další aktivitu soketu.
V následujícím příkladu se text přijatý ze socketu zobrazí v informační zprávě.
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;
- Pokud byla událost vyvolána, protože vypršela platnost časovače uchování, měl by váš kód odeslat některá data přes soket, aby byl soket aktivní a restartoval časovač uchování. Opět je důležité vrátit vlastnictví soketu správci soketů, aby bylo možné přijímat další oznámení o událostech:
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;
- Pokud byla událost vyvolána kvůli uzavření soketu, znovu navažte spojení soketu a ujistěte se, že po vytvoření nového soketu na správce soketu převedete jeho vlastnictví. V této ukázce se název hostitele a port ukládají v místním nastavení, aby bylo možné je použít k navázání nového připojení soketu:
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;
- Po dokončení zpracování oznámení události nezapomeňte dokončit odložení:
deferral.Complete();
Kompletní ukázku demonstrující použití SocketActivityTriggeru a zprostředkovatele soketů najdete v ukázce SocketActivityStreamSocket. Inicializace soketu se provádí v Scenario1_Connect.xaml.cs a implementace úlohy na pozadí je v SocketActivityTask.cs.
Pravděpodobně si všimnete, že ukázka volá TransferOwnership , jakmile vytvoří nový soket nebo získá existující soket, místo použití obslužné rutiny OnSuspending dokonce tak, jak je popsáno v tomto tématu. Důvodem je to, že se ukázka zaměřuje na předvedení SocketActivityTriggeru a nepoužívá soket pro žádnou jinou aktivitu, když je spuštěná. Vaše aplikace bude pravděpodobně složitější a měla by použít OnSuspending k určení, kdy volat TransferOwnership.
Spouštěče řídicího kanálu
Nejprve se ujistěte, že správně používáte spouštěče řídicích kanálů (CCT). Pokud používáte připojení DatagramSocket, StreamSocket nebo StreamSocketListener , doporučujeme použít SocketActivityTrigger. Pro StreamSocket lze použít CCTs, ale spotřebovávají více prostředků a v režimu Connected Standby nemusí fungovat.
Pokud používáte WebSockets, IXMLHTTPRequest2, System.Net.Http.HttpClient nebo Windows. Web.Http.HttpClient, pak musíte použít ControlChannelTrigger.
ControlChannelTrigger pomocí WebSockets
Important
Funkce popsaná v této části (ControlChannelTrigger s WebSockets) je podporována v sadě Windows SDK verze 10.0.15063.0 a novější.
Při použití MessageWebSocket nebo StreamWebSocket s ControlChannelTrigger platí některé zvláštní aspekty. Existují některé vzory použití specifické pro přenos a osvědčené postupy, které byste měli dodržovat při použití MessageWebSocket nebo StreamWebSocket s ControlChannelTrigger. Kromě toho tyto aspekty ovlivňují způsob zpracování požadavků na příjem paketů na StreamWebSocket . Požadavky na příjem paketů na MessageWebSocket nejsou ovlivněny.
Při použití MessageWebSocket nebo StreamWebSocket s ControlChannelTrigger by se měly dodržovat následující vzory použití a osvědčené postupy:
- Vynikající příjem soketů musí být vždy vystaven. To je nutné, aby mohly probíhat úlohy push oznámení.
- Protokol WebSocket definuje standardní model pro zprávy keep-alive. Třída WebSocketKeepAlive může odesílat zprávy protokolu WebSocket iniciované klientem na server. Třída WebSocketKeepAlive by měla být registrována jako TaskEntryPoint pro KeepAliveTrigger aplikací.
Některé zvláštní aspekty ovlivňují způsob zpracování požadavků na příjem paketů na StreamWebSocket . Konkrétně při použití StreamWebSocket s ControlChannelTrigger musí vaše aplikace používat nezpracovaný asynchronní vzor pro zpracování čtení místo modelu await v jazyce C# a VB.NET nebo Tasks v jazyce C++. Nezpracovaný asynchronní vzor je znázorněn v ukázce kódu dále v této části.
Použití nezpracovaného vzoru async umožňuje systému Windows synchronizovat metodu IBackgroundTask.Run v úloze na pozadí pro ControlChannelTrigger s návratem zpětného volání po dokončení příjmu. Metoda Run je volána poté, co se vrátí zpětné volání po dokončení. Tím se zajistí, že aplikace obdržela data nebo chyby před vyvolání metody Run .
Je důležité si uvědomit, že aplikace musí zahájit další operaci čtení dříve, než v callbacku dokončení předá řízení zpět. Je také důležité poznamenat, že DataReader nelze přímo použít s transportem MessageWebSocket nebo StreamWebSocket, protože by to narušilo synchronizaci popsanou výše. Nepodporuje se použití metody DataReader.LoadAsync přímo nad přenosem. Místo toho lze IBuffer vrácený metodou IInputStream.ReadAsync u vlastnosti StreamWebSocket.InputStream později předat metodě DataReader.FromBuffer k dalšímu zpracování.
Následující ukázka ukazuje, jak použít nezpracovaný asynchronní vzor pro zpracování čtení ve StreamWebSocketu.
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);
}
}
Obslužná rutina dokončení čtení se zaručeně spustí před tím, než je v úloze na pozadí pro ControlChannelTrigger vyvolána metoda IBackgroundTask.Run. Windows má interní synchronizaci, aby počkal na návrat aplikace ze zpětného volání po dokončení čtení. Aplikace obvykle data nebo chybu z MessageWebSocket nebo StreamWebSocket rychle zpracuje v callbacku po dokončení čtení. Samotná zpráva je zpracována v kontextu IBackgroundTask.Run metoda. V níže uvedené ukázce je tato skutečnost znázorněna použitím fronty zpráv, do které obslužná rutina dokončení čtení vloží zprávu a kterou úloha na pozadí později zpracuje.
Následující ukázka ukazuje obslužnou rutinu dokončení čtení, která se má použít s nezpracovaným asynchronním vzorem pro zpracování čtení ve StreamWebSocketu.
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);
}
Dalším detailem u WebSocketů je obsluha keep-alive. Protokol WebSocket definuje standardní model pro udržovací zprávy.
Pokud používáte MessageWebSocket nebo StreamWebSocket, zaregistrujte instanci třídy WebSocketKeepAlive jako TaskEntryPoint pro trigger KeepAliveTrigger, aby bylo možné aplikaci probudit z pozastaveného stavu a pravidelně odesílat udržovací zprávy serveru (vzdálenému koncovému bodu). To by se mělo provést jako součást kódu aplikace pro registraci na pozadí i v manifestu balíčku.
Tento vstupní bod úkolu Windows. Sockets.WebSocketKeepAlive je potřeba zadat na dvou místech:
- Při vytváření triggeru KeepAliveTrigger ve zdrojovém kódu (viz příklad níže).
- V manifestu balíčku aplikace pro deklarování úlohy na pozadí keepalive.
Následující ukázka přidá do manifestu aplikace pod prvek <Application> oznámení aktivační události sítě a aktivační událost keepalive.
<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>
Aplikace musí být při použití příkazu await v kontextu ControlChannelTriggeru a asynchronní operace na StreamWebSocket, MessageWebSocket nebo StreamSocket velmi opatrní. Objekt Task<bool> lze použít k registraci ControlChannelTrigger pro nabízená oznámení a udržování aktivního připojení WebSocket u StreamWebSocket a k připojení transportu. V rámci registrace je transport StreamWebSocket nastaven jako transport pro ControlChannelTrigger a je zahájeno čtení. Task.Result zablokuje aktuální vlákno, dokud všechny kroky v úloze nespustí a vrátí příkazy v textu zprávy. Úloha není vyřešena, dokud metoda nevrátí hodnotu true nebo false. To zaručuje, že bude provedena celá metoda. Úkol může obsahovat více příkazů await, které jsou chráněny úlohou. Tento vzor by měl být použit s ControlChannelTrigger objekt, když StreamWebSocket nebo MessageWebSocket je použit jako přenos. U operací, které mohou trvat dlouhou dobu (například typická asynchronní operace čtení), by aplikace měla použít nezpracovaný asynchronní vzor, který jsme probírali dříve.
Následující ukázka registruje ControlChannelTrigger pro nabízené oznámení a WebSocket keep-alives na 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;
}
Další informace o použití MessageWebSocket nebo StreamWebSocket s ControlChannelTrigger naleznete v ukázce ControlChannelTrigger StreamWebSocket.
ControlChannelTrigger s objektem HttpClient
Při použití HttpClient s ControlChannelTrigger je potřeba vzít v úvahu některé zvláštní aspekty. Existují některé vzory použití specifické pro přenos a osvědčené postupy, které byste měli dodržovat při použití HttpClient s ControlChannelTrigger. Kromě toho tyto aspekty ovlivňují způsob zpracování požadavků na příjem paketů v HttpClient .
Poznámka:HttpClient používající PROTOKOL SSL se v současné době nepodporuje pomocí funkce síťového triggeru a ControlChannelTrigger. Při použití HttpClient s ControlChannelTriggerem by se měly dodržovat následující vzory použití a osvědčené postupy:
- Aplikace může před odesláním požadavku na konkrétní identifikátor URI nastavit různé vlastnosti a hlavičky v objektu HttpClient nebo HttpClientHandler v oboru názvů System.Net.Http .
- Aplikace možná bude muset před vytvořením transportu HttpClient, který se má používat s ControlChannelTrigger, odeslat úvodní požadavek, aby správně otestovala a nastavila transport. Jakmile aplikace zjistí, že přenos může být správně nastaven, lze objekt HttpClient nakonfigurovat jako transportní objekt použitý s ControlChannelTrigger objektu. Tento proces je navržený tak, aby zabránil některým scénářům v narušení připojení vytvořeného přes přenos. Pokud používáte SSL s certifikátem, aplikace může vyžadovat zobrazení dialogového okna pro zadání kódu PIN nebo v případě, že existuje více certifikátů, ze které si můžete vybrat. Může se vyžadovat ověřování proxy serveru a ověřování serveru. Pokud vyprší platnost ověřování proxy serveru nebo serveru, může být připojení uzavřeno. Jedním ze způsobů, jak může aplikace řešit tyto problémy s vypršením platnosti ověřování, je nastavit časovač. Pokud je vyžadováno přesměrování HTTP, není zaručeno, že druhé připojení lze spolehlivě navázat. Počáteční testovací požadavek zajistí, aby aplikace před použitím objektu HttpClient jako přenosu s objektem ControlChannelTrigger používala nejvíce up-to-date přesměrovanou adresu URL.
Na rozdíl od jiných síťových přenosů nelze objekt HttpClient předat přímo do UsingTransport metoda ControlChannelTrigger objektu. Místo toho musí být objekt HttpRequestMessage speciálně vytvořen pro použití s HttpClient objekt a ControlChannelTrigger. HttpRequestMessage objekt je vytvořen pomocí RtcRequestFactory.Create metoda. Objekt HttpRequestMessage, který se vytvoří, je poté předán metodě UsingTransport.
Následující ukázka ukazuje, jak vytvořit HttpRequestMessage objekt pro použití s HttpClient objekt a 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;
}
}
Některé zvláštní aspekty ovlivňují způsob, jakým požadavky na odesílání požadavků HTTP na HttpClient za účelem zahájení přijímání odpovědi zpracovávají. Konkrétně při použití HttpClient s ControlChannelTrigger musí vaše aplikace použít úlohu pro zpracování odesílání místo modelu await .
Při použití HttpClient nedochází v úloze na pozadí pro ControlChannelTrigger k žádné synchronizaci s metodou IBackgroundTask.Run při návratu zpětného volání po dokončení příjmu. Z tohoto důvodu může aplikace použít pouze blokující metodu HttpResponseMessage v metodě Run a čekat na přijetí celé odpovědi.
Použití HttpClient s ControlChannelTrigger je výrazně odlišné od StreamSocket, MessageWebSocket nebo StreamWebSocket přenosy . Zpětné volání pro příjem HttpClient je doručeno aplikaci prostřednictvím úlohy, protože kód HttpClient. To znamená, že úloha push oznámení ControlChannelTrigger se spustí, jakmile budou aplikaci doručena data nebo chyba. V následující ukázce kód ukládá proměnnou responseTask vrácenou metodou HttpClient.SendAsync do globálního úložiště, odkud ji úloha push oznámení převezme a zpracuje přímo v kódu.
Následující ukázka ukazuje, jak zpracovat požadavky na HttpClient při použití s 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);
}
Následující ukázka ukazuje, jak číst odpovědi přijaté v HttpClient při použití s 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;
}
Další informace o použití HttpClient s ControlChannelTrigger najdete v ukázce ControlChannelTrigger HttpClient.
ControlChannelTrigger s využitím IXMLHttpRequest2
Při použití IXMLHTTPRequest2 s ControlChannelTriggerem platí některé zvláštní aspekty. Existují některé vzory použití specifické pro přenos a osvědčené postupy, které by se měly dodržovat při použití IXMLHTTPRequest2 s ControlChannelTrigger. Použití ControlChannelTrigger nemá vliv na způsob zpracování požadavků na odesílání nebo přijímání požadavků HTTP v rámci IXMLHTTPRequest2.
Vzory použití a osvědčené postupy při použití IXMLHTTPRequest2 s ControlChannelTriggerem
- Objekt IXMLHTTPRequest2 , pokud se používá jako přenos, má životnost pouze jednoho požadavku/odpovědi. Při použití s objektem ControlChannelTrigger je vhodné objekt ControlChannelTrigger jednou vytvořit a nakonfigurovat a potom opakovaně volat metodu UsingTransport, přičemž se při každém volání přidruží nový objekt IXMLHTTPRequest2. Aplikace by měla před zadáním nového objektu IXMLHTTPRequest2 odstranit předchozí objekt IXMLHTTPRequest2, aby se zajistilo, že aplikace nepřekročí přidělené limity prostředků.
- Aplikace může před voláním metody Send volat metody SetProperty a SetRequestHeader pro nastavení přenosu HTTP.
- Aplikace může před vytvořením přenosu, který se má použít s ControlChannelTriggerem, muset provést počáteční požadavek Odeslat, aby mohla testovat a správně nastavit přenos. Jakmile aplikace zjistí, že je přenos správně nastaven, lze objekt IXMLHTTPRequest2 nakonfigurovat jako transportní objekt použitý s ControlChannelTrigger. Tento proces je navržený tak, aby zabránil některým scénářům v narušení připojení vytvořeného přes přenos. Pokud používáte SSL s certifikátem, aplikace může vyžadovat zobrazení dialogového okna pro zadání kódu PIN nebo v případě, že existuje více certifikátů, ze které si můžete vybrat. Může se vyžadovat ověřování proxy serveru a ověřování serveru. Pokud vyprší platnost autentizace proxy serveru nebo serveru, může být připojení uzavřeno. Jedním ze způsobů, jak může aplikace řešit tyto problémy s vypršením platnosti ověřování, je nastavit časovač. Pokud je vyžadováno přesměrování HTTP, není zaručeno, že druhé připojení lze spolehlivě navázat. Počáteční testovací požadavek zajistí, aby aplikace před použitím objektu IXMLHTTPRequest2 jako přenosu s objektem ControlChannelTrigger používala nejvíce up-to-date přesměrovanou adresu URL.
Další informace o použití IXMLHTTPRequest2 s ControlChannelTrigger najdete v ukázce ControlChannelTrigger s IXMLHTTPRequest2.
Důležitá rozhraní API
Windows developer