Principy a zpracování událostí doby platnosti v knihovně SignalR

Upozornění

Tato dokumentace není určená pro nejnovější verzi SignalR. Podívejte se na ASP.NET Core SignalR.

Tento článek obsahuje přehled událostí připojení, opětovného připojení a odpojení služby SignalR, které můžete zpracovat, a nastavení časového limitu a udržování, které můžete nakonfigurovat.

Článek předpokládá, že už máte určité znalosti o signalR a událostech životnosti připojení. Úvod ke službě SignalR najdete v tématu Úvod do Služby SignalR. Seznam událostí životnosti připojení najdete v následujících zdrojích informací:

Verze softwaru použité v tomto tématu

Předchozí verze tohoto tématu

Informace o starších verzích služby SignalR najdete v tématu Starší verze služby SignalR.

Dotazy a komentáře

Pošlete nám prosím zpětnou vazbu k tomu, jak se vám tento kurz líbil a co bychom mohli vylepšit v komentářích v dolní části stránky. Pokud máte dotazy, které přímo nesouvisejí s kurzem, můžete je publikovat na fóru ASP.NET SignalR nebo StackOverflow.com.

Přehled

Tento článek obsahuje následující části:

Odkazy na referenční témata rozhraní API jsou na verzi rozhraní API .NET 4.5. Pokud používáte .NET 4, projděte si témata týkající se rozhraní API verze .NET 4.

Terminologie a scénáře doby života připojení

Obslužná rutina OnReconnected události v centru SignalR hub může pro daného klienta provést přímo po OnConnected , ale ne po OnDisconnected ní. Důvodem, proč můžete mít opětovné připojení bez odpojení, je několik způsobů, jak se slovo "připojení" používá v SignalR.

Připojení SignalR, přenosová připojení a fyzická připojení

Tento článek bude rozlišovat mezi připojeními SignalR, dopravními afyzickými připojeními:

  • Připojení SignalR odkazuje na logický vztah mezi klientem a adresou URL serveru, který je udržován rozhraním SIGNALR API a jednoznačně identifikovaný ID připojení. Data o této relaci jsou spravována službou SignalR a používají se k vytvoření přenosového připojení. Relace se ukončí a SignalR odstraní data, když klient zavolá metodu Stop nebo dojde k dosažení časového limitu, zatímco se SignalR pokouší znovu navázat ztracené přenosové připojení.
  • Přenosové připojení označuje logický vztah mezi klientem a serverem, který udržuje jedno ze čtyř přenosových rozhraní API: WebSocket, události odeslané serverem, rámec forever nebo dlouhé dotazování. SignalR používá rozhraní API pro přenos k vytvoření přenosového připojení a rozhraní API pro přenos závisí na existenci fyzického síťového připojení. Přenosové připojení se ukončí, když ho signalR ukončí nebo když rozhraní API pro přenos zjistí, že fyzické připojení je přerušené.
  • Fyzické připojení označuje fyzická síťová propojení – vodiče, bezdrátové signály, směrovače atd. - usnadňují komunikaci mezi klientským počítačem a serverovým počítačem. Fyzické připojení musí být přítomno, aby bylo možné vytvořit dopravní spojení, a aby bylo možné navázat připojení SignalR, musí být vytvořeno dopravní spojení. Přerušení fyzického připojení však ne vždy okamžitě ukončí přenosové připojení nebo připojení SignalR, jak bude vysvětleno dále v tomto tématu.

V následujícím diagramu je připojení SignalR reprezentováno rozhraním Hubs API a vrstvou SignalR rozhraní API PersistentConnection, přenosové připojení je reprezentováno vrstvou Transports a fyzické připojení je reprezentováno spojnicemi mezi serverem a klienty.

Diagram architektury SignalR

Při volání Start metody v klientovi SignalR poskytujete kód klienta SignalR se všemi informacemi, které potřebuje k navázání fyzického připojení k serveru. Kód klienta SignalR používá tyto informace k vytvoření požadavku HTTP a navázání fyzického připojení, které používá jednu ze čtyř metod přenosu. Pokud se přenosové připojení nezdaří nebo server selže, připojení SignalR neodejde okamžitě, protože klient stále má informace potřebné k automatickému opětovnému navázání nového přenosového připojení na stejnou adresu URL signalR. V tomto scénáři není zahrnut žádný zásah z uživatelské aplikace a když kód klienta SignalR naváže nové přenosové připojení, nezačne nové připojení SignalR. Kontinuita připojení SignalR se odráží ve skutečnosti, že ID připojení, které se vytvoří při volání Start metody, se nezmění.

Obslužná rutina OnReconnected události v centru se spustí, když se po ztrátě automaticky znovu vytvoří přenosové připojení. Obslužná rutina OnDisconnected události se spustí na konci připojení SignalR. Připojení SignalR může skončit některým z následujících způsobů:

  • Pokud klient zavolá metodu Stop , odešle se na server zpráva o zastavení a klient i server okamžitě ukončí připojení SignalR.
  • Po ztrátě připojení mezi klientem a serverem se klient pokusí znovu připojit a server čeká, až se klient znovu připojí. Pokud jsou pokusy o opětovné připojení neúspěšné a časový limit odpojení skončí, klient i server ukončí připojení SignalR. Klient se přestane pokoušet o opětovné připojení a server odstraní svou reprezentaci připojení SignalR.
  • Pokud klient přestane běžet bez možnosti volat metodu Stop , server počká, až se klient znovu připojí, a poté ukončí připojení SignalR po uplynutí časového limitu odpojení.
  • Pokud server přestane běžet, klient se pokusí znovu připojit (znovu vytvořit přenosové připojení) a po uplynutí časového limitu odpojení ukončí připojení SignalR.

Pokud nedojde k žádným problémům s připojením a uživatelská aplikace ukončí připojení SignalR voláním Stop metody, připojení SignalR a přenosové připojení začínají a končí přibližně ve stejnou dobu. Další scénáře jsou podrobněji popsány v následujících částech.

Scénáře odpojení přenosu

Fyzická připojení můžou být pomalá nebo může docházet k přerušení připojení. V závislosti na faktorech, jako je délka přerušení, může být přepravní spojení ukončeno. SignalR se pak pokusí znovu navázat přenosové připojení. Někdy rozhraní API přenosového připojení zjistí přerušení a přeruší přenosové připojení a SignalR okamžitě zjistí, že připojení bylo ztraceno. V jiných scénářích rozhraní API pro přenos ani SignalR okamžitě neznají, že došlo ke ztrátě připojení. Pro všechny přenosy s výjimkou dlouhého dotazování klient SignalR používá funkci s názvem keepalive ke kontrole ztráty připojení, kterou rozhraní API pro přenos nedokáže rozpoznat. Informace o dlouhých dotazovacích připojeních najdete v části Nastavení vypršení časového limitu a uchování dál v tomto tématu.

Pokud je připojení neaktivní, server pravidelně odesílá klientovi paket keepalive. K datu psaní tohoto článku je výchozí frekvence každých 10 sekund. Nasloucháním těchto paketů můžou klienti zjistit, jestli nedošlo k problému s připojením. Pokud se paket keepalive nepřinese podle očekávání, klient po krátké době předpokládá, že dochází k problémům s připojením, jako je zpomalení nebo přerušení. Pokud se příkaz keepalive ani po delší době neobdrží, klient předpokládá, že připojení bylo ukončeno, a začne se pokoušet o opětovné připojení.

Následující diagram znázorňuje události klienta a serveru, které jsou vyvolány v typickém scénáři, když dojde k problémům s fyzickým připojením, které rozhraní API pro přenos okamžitě nerozpozná. Diagram se vztahuje na následující okolnosti:

  • Přenos je WebSocket, forever frame nebo události odeslané serverem.
  • Fyzické síťové připojení má různá období přerušení.
  • Rozhraní API pro přenos si o přerušeních neuvědomuje, takže SignalR spoléhá při jejich detekci na funkci keepalive.

Odpojení přenosu

Pokud klient přejde do režimu opětovného připojení, ale nemůže navázat přenosové připojení v rámci časového limitu odpojení, server ukončí připojení SignalR. Když k tomu dojde, server spustí metodu centra OnDisconnected a zařadí zprávu o odpojení, která se odešle klientovi pro případ, že se klient později připojí. Pokud se klient znovu připojí, přijme příkaz pro odpojení a zavolá metodu Stop . V tomto scénáři OnReconnected se nespustí, když se klient znovu připojí, a OnDisconnected nespustí se, když klient zavolá Stop. Následující diagram znázorňuje tento scénář.

Přerušení přenosu – vypršení časového limitu serveru

Události životnosti připojení SignalR, které mohou být vyvolány na klientovi, jsou následující:

  • ConnectionSlow událost klienta.

    Vyvolá se v případě, že od poslední zprávy nebo přijetí příkazu ping pro uchování uplynula přednastavená část časového limitu keepalive. Výchozí období upozornění na časový limit keepalive je 2/3 časového limitu keepalive. Časový limit keepalive je 20 sekund, takže k upozornění dochází přibližně po 13 sekundách.

    Ve výchozím nastavení server odesílá příkazy ping typu keepalive každých 10 sekund a klient kontroluje příkazy ping typu keepalive přibližně každé 2 sekundy (jedna třetina rozdílu mezi hodnotou časového limitu keepalive a hodnotou upozornění časového limitu keepalive).

    Pokud se rozhraní API pro přenos dozví o odpojení, signalR může být o odpojení informován před uplynutím období upozornění na časový limit keepalive. V takovém případě ConnectionSlow by událost nebyla vyvolána a SignalR by přešla přímo na Reconnecting událost.

  • Reconnecting událost klienta.

    Vyvolána, když (a) rozhraní API pro přenos zjistí, že došlo ke ztrátě připojení, nebo (b) od přijetí poslední zprávy nebo příkazu ping pro uchování uplynul časový limit keepalive. Kód klienta SignalR se začne pokoušet znovu připojit. Tuto událost můžete zpracovat, pokud chcete, aby vaše aplikace při ztrátě přenosového připojení určitou akci podnikla. Výchozí časový limit keepalive je aktuálně 20 sekund.

    Pokud se kód klienta pokusí volat metodu Centra, když je SignalR v režimu opětovného připojení, signalR se pokusí odeslat příkaz. Tyto pokusy většinou selžou, ale za určitých okolností můžou být úspěšné. V případě událostí odeslaných serverem, nekonečného rámce a dlouhých přenosů dotazování používá SignalR dva komunikační kanály: jeden, který klient používá k odesílání zpráv, a druhý, který používá k příjmu zpráv. Kanál, který se používá pro příjem, je trvale otevřený, a to je kanál, který se při přerušení fyzického připojení zavře. Kanál použitý pro odesílání zůstává dostupný, takže pokud se obnoví fyzické připojení, volání metody z klienta na server může být úspěšné před opětovným navázáním kanálu příjmu. Vrácená hodnota nebude přijata, dokud SignalR znovu neotevře kanál používaný k příjmu.

  • Reconnected událost klienta.

    Vyvolá se při opětovném navázání dopravního spojení. Obslužná rutina OnReconnected události v centru se spustí.

  • Closed událost klienta (disconnected událost v JavaScriptu).

    Vyvolána při vypršení časového limitu odpojení, zatímco se kód klienta SignalR pokouší znovu připojit po ztrátě přenosového připojení. Výchozí časový limit odpojení je 30 sekund. (Tato událost je vyvolána také při ukončení připojení, Stop protože je volána metoda.)

Přerušení přenosového připojení, která nejsou zjištěna rozhraním API pro přenos a nezpozdí příjem příkazů ping typu keepalive ze serveru o delší dobu, než je období upozornění na vypršení časového limitu keepalive, nemusí způsobit vyvolání událostí životnosti připojení.

Některá síťová prostředí záměrně ukončují nečinná připojení a další funkcí paketů keepalive je pomoct tomu zabránit tím, že těmto sítím dají vědět, že se používá připojení SignalR. V extrémních případech nemusí výchozí frekvence keepalive ping stačit k tomu, aby se zabránilo zavřeným připojením. V takovém případě můžete nakonfigurovat, aby se příkazy ping keepalive odesílaly častěji. Další informace najdete v části Vypršení časového limitu a nastavení udržování dál v tomto tématu.

Poznámka

Důležité: Pořadí zde popsaných událostí není zaručeno. SignalR se podle tohoto schématu snaží vyvolat události životnosti připojení předvídatelným způsobem, ale existuje mnoho variant síťových událostí a mnoho způsobů, jak je základní komunikační architektury, jako jsou rozhraní API pro přenos, zpracovávají. Například událost nemusí být vyvolána, Reconnected když se klient znovu připojí, nebo OnConnected obslužná rutina na serveru může být spuštěna, když pokus o navázání připojení není úspěšný. Toto téma popisuje pouze účinky, které by normálně byly způsobeny určitými typickými okolnostmi.

Scénáře odpojení klienta

V klientovi prohlížeče se kód klienta SignalR, který udržuje připojení SignalR, spouští v kontextu JavaScriptu webové stránky. Proto se připojení SignalR musí ukončit, když přejdete z jedné stránky na jinou, a proto máte více připojení s více ID připojení, pokud se připojujete z více oken prohlížeče nebo karet. Když uživatel zavře okno nebo kartu prohlížeče, přejde na novou stránku nebo aktualizuje stránku, připojení SignalR okamžitě skončí, protože kód klienta SignalR zpracovává tuto událost prohlížeče za vás a volá metodu Stop . V těchto scénářích nebo v jakékoli klientské platformě, když vaše aplikace volá metodu Stop , OnDisconnected obslužná rutina události se okamžitě spustí na serveru a klient vyvolá Closed událost (událost má název disconnected v JavaScriptu).

Pokud se klientská aplikace nebo počítač, na kterém je spuštěná, zhroutí nebo přejde do režimu spánku (například když uživatel zavře přenosný počítač), server nebude informován o tom, co se stalo. Pokud server ví, může být ztráta klienta způsobená přerušením připojení a klient se může pokoušet o opětovné připojení. Proto v těchto scénářích server čeká, aby se klient mohl znovu připojit, a OnDisconnected nespustí se, dokud nevyprší časový limit odpojení (ve výchozím nastavení přibližně 30 sekund). Následující diagram znázorňuje tento scénář.

Selhání klientského počítače

Scénáře odpojení serveru

Když server přejde do offline režimu – restartuje se, selže, doména aplikace se recykluje atd. – Výsledek se může podobat ztrátě připojení nebo rozhraní API pro přenos a SignalR můžou okamžitě vědět, že server je pryč, a služba SignalR se může začít pokoušet o opětovné připojení bez vyvolání ConnectionSlow události. Pokud klient přejde do režimu opětovného připojení a pokud se server obnoví nebo restartuje nebo se před vypršením časového limitu odpojení přepojí nový server do režimu online, klient se znovu připojí k obnoveným nebo novému serveru. V takovém případě bude připojení SignalR pokračovat na klientovi a vyvolá se Reconnected událost. Na prvním serveru OnDisconnected se nikdy nespustí a na novém serveru se spustí, OnReconnected i když OnConnected se pro daného klienta na daném serveru předtím nikdy nespustí. (Efekt je stejný, pokud se klient po restartování nebo recyklaci domény aplikace znovu připojí ke stejnému serveru, protože při restartování serveru nemá žádnou paměť na předchozí aktivitu připojení.) Následující diagram předpokládá, že rozhraní API pro přenos se okamžitě dozví o ztraceném připojení, takže ConnectionSlow se událost nevyvolá.

Selhání serveru a opětovné připojení

Pokud server není dostupný během časového limitu odpojení, připojení SignalR se ukončí. V tomto scénáři Closed je událost (disconnected v klientech JavaScriptu) vyvolána na klientovi, ale OnDisconnected na serveru se nikdy nevolá. V následujícím diagramu se předpokládá, že rozhraní API pro přenos se o ztraceném připojení nedozví, takže ho detekuje funkce keepalive služby SignalR a ConnectionSlow vyvolá událost.

Selhání serveru a vypršení časového limitu

Časový limit a nastavení zachování

Výchozí ConnectionTimeouthodnoty , DisconnectTimeouta KeepAlive jsou vhodné pro většinu scénářů, ale je možné je změnit, pokud má vaše prostředí zvláštní potřeby. Pokud například vaše síťové prostředí ukončí připojení, která jsou nečinná po dobu 5 sekund, budete muset snížit hodnotu keepalive.

Connectiontimeout

Toto nastavení představuje dobu, po kterou má dopravní připojení nechat otevřené a čekat na odpověď před jeho zavřením a otevřením nového připojení. Výchozí hodnota je 110 sekund.

Toto nastavení platí pouze v případě, že je zakázaná funkce keepalive, což se obvykle vztahuje pouze na dlouhý přenos dotazování. Následující diagram znázorňuje účinek tohoto nastavení na dlouhé přenosové spojení dotazování.

Transportní spojení s dlouhým dotazováním

DisconnectTimeout

Toto nastavení představuje dobu čekání po ztrátě přenosového připojení před vyvolání Disconnected události. Výchozí hodnota je 30 sekund. Když nastavíte DisconnectTimeout, KeepAlive automaticky se nastaví na 1/3 DisconnectTimeout hodnoty.

Keepalive

Toto nastavení představuje dobu čekání před odesláním paketu keepalive přes nečinné připojení. Výchozí hodnota je 10 sekund. Tato hodnota nesmí být větší než 1/3 DisconnectTimeout hodnoty.

Pokud chcete nastavit jak, tak DisconnectTimeout i KeepAlive, nastavte KeepAlive za DisconnectTimeout. V opačném případě se vaše KeepAlive nastavení přepíše, když DisconnectTimeout se automaticky nastaví KeepAlive na 1/3 hodnoty časového limitu.

Pokud chcete zakázat funkci keepalive, nastavte KeepAlive na hodnotu null. Funkce keepalive se pro dlouhý přenos dotazování automaticky zakáže.

Jak změnit časový limit a zachovat nastavení

Pokud chcete změnit výchozí hodnoty pro tato nastavení, nastavte je v Application_Start souboru Global.asax , jak je znázorněno v následujícím příkladu. Hodnoty zobrazené v ukázkovém kódu jsou stejné jako výchozí hodnoty.

protected void Application_Start(object sender, EventArgs e)
{
    // Make long polling connections wait a maximum of 110 seconds for a
    // response. When that time expires, trigger a timeout command and
    // make the client reconnect.
    GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
    
    // Wait a maximum of 30 seconds after a transport connection is lost
    // before raising the Disconnected event to terminate the SignalR connection.
    GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
    
    // For transports other than long polling, send a keepalive packet every
    // 10 seconds. 
    // This value must be no more than 1/3 of the DisconnectTimeout value.
    GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
    
    RouteTable.Routes.MapHubs();
}

Jak uživatele upozornit na odpojení

V některých aplikacích můžete chtít uživateli zobrazit zprávu, když dojde k problémům s připojením. Máte několik možností, jak a kdy to udělat. Následující ukázky kódu jsou určené pro javascriptového klienta používajícího vygenerovaný proxy server.

  • connectionSlow Zpracování události tak, aby se zobrazila zpráva, jakmile signalR bude vědět o problémech s připojením, před tím, než přejde do režimu opětovného připojení.

    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // Your function to notify user.
    });
    
  • reconnecting Zpracování události tak, aby se zobrazila zpráva, když SignalR ví o odpojení a přejde do režimu opětovného připojení.

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // Your function to notify user.
    });
    
  • disconnected Zpracování události tak, aby se zobrazila zpráva, když vypršel časový limit pokusu o opětovné připojení. V tomto scénáři je jediným způsobem, jak znovu navázat připojení se serverem, restartování připojení SignalR voláním Start metody, která vytvoří nové ID připojení. Následující ukázka kódu používá příznak, který zajistí, že vydáte oznámení pouze po vypršení časového limitu opětovného připojení, ne po normálním ukončení připojení SignalR způsobeném Stop voláním metody.

    var tryingToReconnect = false;
    
    $.connection.hub.reconnecting(function() {
        tryingToReconnect = true;
    });
    
    $.connection.hub.reconnected(function() {
        tryingToReconnect = false;
    });
    
    $.connection.hub.disconnected(function() {
        if(tryingToReconnect) {
            notifyUserOfDisconnect(); // Your function to notify user.
        }
    });
    

Jak se nepřetržitě znovu připojovat

V některých aplikacích můžete chtít automaticky znovu navázat připojení po jeho ztrátě a vypršení časového limitu pokusu o opětovné připojení. K tomu můžete volat metodu Start z Closed obslužné rutiny události (disconnected obslužná rutina události na klientech JavaScriptu). Před voláním Start můžete nějakou dobu počkat, abyste se tomu vyhnuli příliš často, když server nebo fyzické připojení nejsou k dispozici. Následující ukázka kódu je určená pro javascriptového klienta používajícího vygenerovaný proxy server.

$.connection.hub.disconnected(function() {
   setTimeout(function() {
       $.connection.hub.start();
   }, 5000); // Restart connection after 5 seconds.
});

Potenciálním problémem, o kterém je potřeba vědět v mobilních klientech, je to, že pokusy o průběžné opětovné připojení, když není k dispozici server nebo fyzické připojení, můžou způsobit zbytečné vybití baterie.

Jak odpojit klienta v kódu serveru

SignalR verze 2 nemá integrované serverové rozhraní API pro odpojení klientů. V budoucnu plánujeme tuto funkci přidat. V aktuální verzi SignalR je nejjednodušší způsob, jak odpojit klienta od serveru, implementovat metodu odpojení na klientovi a volat tuto metodu ze serveru. Následující ukázka kódu ukazuje metodu odpojení pro klienta JavaScriptu pomocí vygenerovaného proxy serveru.

var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
    $.connection.hub.stop();
};

Upozornění

Zabezpečení – Tato metoda pro odpojení klientů ani navrhované integrované rozhraní API neřeší scénář napadených klientů se škodlivým kódem, protože klienti se mohou znovu připojit nebo napadený kód může metodu stopClient odebrat nebo změnit to, co dělá. Vhodným místem pro implementaci stavové ochrany proti odepření služby (DOS) není architektura nebo serverová vrstva, ale spíše front-endová infrastruktura.

Zjištění důvodu odpojení

SignalR 2.1 přidá do události serveru OnDisconnect přetížení, které indikuje, jestli se klient záměrně odpojil místo vypršení časového limitu. Parametr StopCalled je true, pokud klient explicitně ukončil připojení. Pokud v JavaScriptu došlo k chybě serveru, která způsobila odpojení klienta, informace o chybě se předají klientovi jako $.connection.hub.lastError.

Kód serveru C#: stopCalled parametr

public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
    if (stopCalled)
    {
        Console.WriteLine(String.Format("Client {0} explicitly closed the connection.", Context.ConnectionId));
    }
    else
    {
        Console.WriteLine(String.Format("Client {0} timed out .", Context.ConnectionId));
    }
            
    return base.OnDisconnected(stopCalled);
}

Kód klienta JavaScriptu: přístup lastError k disconnect události.

$.connection.hub.disconnected(function () {
    if ($.connection.hub.lastError) 
        { alert("Disconnected. Reason: " +  $.connection.hub.lastError.message); }
});