Komunikace v reálném čase díky Server-Sent Events a Web Sockets

11. března 2015

AJAX nikdy nebyl jedinou možností, jak aktualizovat obsah webových stránek bez nutnosti jejich znovunačtení. Dalo se to provést ještě dynamickým vytvářením elementu script s externím zdrojem (JSONP) nebo kořistěním dat z neviditelného a skrytě aktualizovaného iframe či jiným podobným způsobem. AJAX byl ale nejpraktičtější. Jenže i AJAX má svou pověstnou Achillovu patu – tu představuje komunikace v reálném čase. AJAX ji neumí rozumným způsobem zajistit, přitom část tzv. RIAs (Rich Internet Applications) na ní je závislá. Význam těchto moderních aplikací nakonec vedl k realizaci sofistikovanějšího modelu komunikace mezi serverem a klienty.

Jak již bylo zmíněno v předchozím dílu, zaměřeném na řešení nedostatků AJAXu, proces výměny dat mezi webovou stránkou a serverem probíhá zpravidla podle HTTP protokolu. HTTP umožňuje pouze jednosměrnou komunikaci: klient inicializuje spojení se serverem a zašle požadavek, který server následně zpracuje, odešle odpověď a ukončí spojení. Pokud tedy klientská část aplikace očekávala data ze serveru, musel vždy vzniknout nejprve požadavek z její strany. Ten se buď v pravidelných intervalech opakoval (polling alias heartbeat), nebo server udržoval spojení do doby, než měl požadovaná data k dispozici (long-polling, streaming).

Konkrétních technik pro komunikaci v reálném čase byla vynalezena celá řada a dostaly souhrnné pojmenování – Comet; název vychází ze schématické představy serveru jako jádra komety s hustým a dlouhým ohonem vláken neukončených spojení. Nicméně starší techniky jsou dnes již, dá-li se to tak říct, překonané a ustupuje se i od AJAXu. W3C specifikovalo rovnou dvoje řešení pro zjednodušení a zefektivnění průběhu komunikace v reálném čase.

Server-Sent Events

Server-Sent Events nepředstavují komplexní řešení, jsou vlastně jen příspěvkem k ulehčení těžké práce chudáků programátorů klientských skriptů. V podstatě se jedná o implementací long-pollingu v prohlížeči. Díky tomu, že se prohlížeč sám stará o některé věci, především o základní roztřízení došlých dat nebo také o obnovu ukončeného spojení, může být kód skriptu úspornější a přehlednější.

Serverem zasílané informace musí mít zvláštní MIME typ – text/event-stream – a příslušný tvar:

event: update
data: {"username": "Smoula", "emotion": "angry"}
id: 3

: tohle už je událost message
data: Tak kdy už mi sakra 
data: vrátíš ty prachy?

Každý řádek by měl začínat prefixem s dvojtečkou. Pokud začíná jenom dvojtečkou, tak se jedná o komentář. Úplně prázdný řádek znamená konec události. Výše uvedený kód představuje dvě události. Každá je brána zvlášť.

Seznam prefixů
event: definuje typ události; výchozím typem události je message
data: řetězcový literál bez uvozovek – může být sice rozdělen na více prefixovaných řádků, stále to však bude jediný řetězec
id: ID události
retry: počet milisekund, tuto dobu by měl prohlížeč vyčkat, než se pokusí obnovit spojení; obvykle prohlížeč vyčkává asi 3 sekundy

Aplikace v prohlížeči přebírá servírovaná data následujícím způsobem:

var friends = {
  "Smoula": {"emotion": "happy", "age": 28},
  "Gargamel": {"emotion": "happy", "age": 19}
};
window.onload = function() {
  var source = new EventSource("chat.php");
  source.addEventListener("message",function(e) {
    document.getElementById("zprava").textContent = e.data;
  },false);
  source.addEventListener("update",function(e) {
    var kamarad=JSON.parse(e.data);
    friends[kamarad.username].emotion = kamarad.emotion;
  },false);
};

Konstruktor EventSource() může mít dva argumenty: prvním je URL a tím druhým (nepovinným) v podobě logické hodnoty se povoluje režim CORS.

Metody a vlastnosti objektu EventSource
addEventListener() Metoda známá z DOM, zde je využita k registraci definovaných událostí
close() uzavře spojení
url URL serveru; pouze ke čtení
withCredentials udává, zda je použit režim pro CORS; pouze ke čtení
readyState stav připojení k serveru (hodnoty: 0 – probíhá navázání spojení, 1 – spojení otevřeno, 2 – spojení uzavřeno); pouze ke čtení

Dále jsou objektu EventSource atributně přiřazeny ovladače událostí onopen, onmessage, onerror. A to je z rozhraní EventSource všechno, žádný mechanismus, který by umožňoval posílat data na server tu už není. Server-Sent Events se hodí v podstatě jen na zasílání notifikací.

Je také nutno poznamentat, že z hlediska serveru se jedná o klasický long-polling nebo streaming, pouze se změnil formát dat.

Web Sockets

Pojem socket znamená v programátorské terminologii komunikační spojení mezi dvěma koncovými uzly prostřednictvím IP. Socket tedy udržuje pomocí dvojice IP adres a portu stálý kanál pro spojení klienta se serverem. Takto mohou oba koncové body v reálném čase zasílat data simultánně přes jeden socket, což transportní protokol TCP umožňuje. Na bázi aplikační vrstvy však je nezbytné udělat změnu a místo tradičního protokolu HTTP použít protokol WS (Web Socket) nebo pro zabezpečené spojení WSS (Web Socket with SSL) místo HTTPS. HTTP je použit jen pro úvodní výměnu hlaviček, tzv. handshake.

Implementace aplikačního rozhraní WebSocket je v JavaScriptu ustavena jako objekt WebSocket.

var params, mode;
...
var socket = new WebSocket('ws://netgame.cz:210/updates');
socket.onopen = function() {
  this.send(params);
};
socket.onmessage = function(e) {
  params = JSON.parse(e.data);
}
socket.onclose() {
  mode = 1;
  alert("Přepnuto do režimu single player");
}

Konstruktor WebSocket() může mít dva argumenty: prvním je URL a tím druhým (nepovinným) subprotokol, případně pole subprotokolů.

Metody a vlastnosti objektu WebSocket
url URL serveru; pouze ke čtení
protocol vrací prázdný řetězec, nebo použitý subprotokol; pouze ke čtení
readyState stav připojení k serveru (hodnoty: 0 – probíhá navázání spojení, 1 – spojení otevřeno, 2 – spojení uzavíráno, 3 – spojení uzavřeno); pouze ke čtení
bufferedAmount počet bytů dat čekajících na odeslání; pouze ke čtení
binaryType určuje, jak má skript interpretovat binární data; výchozí hodnotu blob, lze změnit na arraybuffer
send() odešle data (řetězec, pole nebo soubor)
close() uzavře spojení

WebSocket pracuje s událostmi open, message, close a error, viz příklad výše.

Ze srovnání představených technologií vychází jednoznačně vítězně Web Sockets: protokol pro obousměrnou komunikaci + jednoduché API, které zvládá vše, co je potřeba. A v neposlední řadě Web Sockets šetří systémové prostředky (paměť, CPU) serveru, které by jinak byly vynakládány na udržení procesu živého spojení.

Odkazy a zdroje

Celý seriál najdete pod štítkem: komunikace mezi stránkou a serverem

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *