WebSocket drží obousměrné spojení, socket.io k němu přidává znovupřipojení a skupiny
Prohlížeč se serveru ptá na novinky pořád dokola: pošle dotaz, dostane odpověď, spojení zavře. U chatu nebo živého přehledu to buď zbytečně zatěžuje, nebo novinku na chvíli prošvihne. WebSocket místo toho drží jedno spojení otevřené oběma směry a socket.io k němu přidává znovupřipojení, skupiny a přechod na starší přenos tam, kde WebSocket neprojde.

Běžná webová stránka se serveru ptá pořád stejně: pošle požadavek, dostane odpověď, spojení se zavře. Když má ale ukazovat něco, co se mění každou chvíli – zprávu v chatu, upozornění, číslo na živém přehledu –, tenhle model naráží. WebSocket ho řeší jinak: otevře jedno spojení a nechá ho otevřené oběma směry.
Proč se opakovaný dotaz nehodí na živá data
Nejjednodušší cesta k „živé“ stránce je HTTP polling: prohlížeč se v pravidelném intervalu ptá, jestli nejsou nová data. Každý dotaz je celý požadavek i s hlavičkami, i když odpověď zní „nic nového“, a interval se nedá nastavit dobře – krátký znamená spoustu prázdných dotazů, dlouhý zprávu se zpožděním. Navíc je to jednosměrné: server čeká, až se stránka zeptá. Šikovnější long polling (server odpověď drží, dokud nemá co poslat) zpoždění odstraní, ale je to jen obcházení toho, že HTTP na obousměrný provoz stavěné nebylo.
WebSocket: jedno spojení oběma směry
WebSocket je samostatný protokol popsaný v RFC 6455 (vydané v prosinci 2011). Spojení začíná jako obyčejný HTTP požadavek, který serveru řekne „přepni tohle spojení na WebSocket“ (handshake). Když server souhlasí, totéž TCP spojení zůstane otevřené a obě strany po něm můžou kdykoli posílat zprávy – bez nových hlaviček, bez čekání na dotaz. Adresa má schéma ws://, pro šifrovanou variantu wss://.
V prohlížeči se s tím pracuje přes objekt WebSocket: vytvoří se konstruktorem, poslouchají se události open, message, error a close, posílá se metodou send().
// Klient v prohlížeci
const socket = new WebSocket("wss://example.com/chat");
socket.addEventListener("open", () => {
socket.send("ahoj");
});
socket.addEventListener("message", (event) => {
console.log("prislo:", event.data);
});
socket.addEventListener("close", () => {
console.log("spojeni se zavrelo");
});
Stav spojení drží vlastnost readyState. MDN u toho upozorňuje na jednu věc: WebSocket API neumí takzvaný backpressure – když zprávy chodí rychleji, než je stránka stíhá zpracovat, hromadí se v paměti a tempo si musí program ohlídat sám. Server se píše v Node.js, Pythonu nebo čemkoli jiném přes knihovnu pro daný jazyk.
Co k WebSocketu přidává socket.io
Holý WebSocket funguje, ale pár věcí neřeší: co když spojení spadne, co když ho podniková proxy nebo antivirus vůbec nepustí a jak zprávy rozlišovat podle druhu. Tyhle mezery zaplňuje socket.io, knihovna pro Node.js a prohlížeč, která nad WebSocketem staví pohodlnější vrstvu.
Uvnitř se dělí na dvě části. Spodní, Engine.IO, se stará o spojení a výběr přenosu. Má tři: HTTP long-polling, WebSocket a novější WebTransport. Trik je v pořadí: spojení se ve výchozím nastavení naváže nejdřív přes long-polling, který projde skoro všude, a teprve pak se po tichu povýší na WebSocket, pokud to jde. Tomuhle záložnímu chování se říká fallback a je to důvod, proč socket.io funguje i tam, kde čistý WebSocket neprojde. Horní vrstva pak přidává znovupřipojení, frontu neodeslaných zpráv, potvrzování doručení, skupiny (rooms) a jmenné prostory (namespaces).
Server se namontuje na HTTP server a poslouchá událost connection. Zprávy jsou pojmenované události – místo jednoho anonymního proudu dat má každý druh zprávy své jméno, na které se navěsí obsluha.
// Server (Node.js)
const { createServer } = require("node:http");
const { Server } = require("socket.io");
const server = createServer();
const io = new Server(server);
io.on("connection", (socket) => {
console.log("klient se pripojil");
socket.on("zprava", (text) => {
io.emit("zprava", text); // rozesle vsem pripojenym
});
socket.on("disconnect", () => {
console.log("klient odpojen");
});
});
server.listen(3000);
Klient v prohlížeči je zrcadlo: zavolá io(), poslouchá stejné události přes on() a posílá je přes emit().
// Klient v prohlizeci
const socket = io();
socket.on("zprava", (text) => {
// vykresli prichozi zpravu
});
socket.emit("zprava", "ahoj");
Volání io.emit() rozešle událost všem připojeným. To se ale hodí málokdy – zprávu z jedné chatovací místnosti nemají dostat všichni na webu.
Rooms: vysílání jen části klientů
Room je pojmenovaný kanál, do kterého socket vstoupí a zase z něj vystoupí. Je to čistě serverová věc – klient o seznamu svých místností nic neví. Vstup zařídí socket.join(), vysílání jen do dané místnosti pak io.to("nazev").emit(...).
io.on("connection", (socket) => {
socket.join("mistnost-42");
});
// odejde jen do klientu v teto mistnosti
io.to("mistnost-42").emit("zprava", "ahoj mistnosti");
Vysílání z jednoho socketu přes socket.to("nazev").emit(...) dostanou všichni v místnosti kromě odesílatele – přesně to, co chatovací okno potřebuje. Při odpojení socket opustí všechny své místnosti sám. Rooms se hodí i na adresování jednoho uživatele: socket se přidá do místnosti pojmenované jeho identifikátorem a upozornění pak dorazí na všechny jeho karty a zařízení najednou.
Když spadne spojení
Jestli spojení žije, hlídá tlukot srdce (heartbeat): server v pravidelném intervalu (ve výchozím nastavení po zhruba 25 sekundách) pošle paket ping a čeká na pong; když se nevrátí, spojení se považuje za zavřené. Po výpadku se socket.io ve výchozím nastavení sám snaží připojit znovu – opakovaně a s prodlužujícími se odstupy, aby po pádu serveru nepřišla záplava dotazů naráz. Neodeslané zprávy počkají ve frontě.
Kdy stačí jednosměrné SSE
WebSocket ani socket.io nejsou vždycky potřeba. Když data tečou jen jedním směrem, ze serveru do prohlížeče, stačí lehčí nástroj: Server-Sent Events (SSE). Jede po obyčejném HTTP, v prohlížeči se obsluhuje objektem EventSource a znovupřipojení zvládá samo. Podle MDN je to jednosměrné spojení – klient přes něj serveru nic poslat nemůže.
const zdroj = new EventSource("/udalosti");
zdroj.onmessage = (event) => {
console.log(event.data);
};
Na živé skóre, průběžné výsledky výpočtu, tok upozornění nebo streamování logu je SSE často přesně akorát: méně kódu než WebSocket a projde i přes prostředí, kde WebSocket vázne. Jakmile ale potřebujete posílat data i od klienta – typicky psát zprávy –, jednosměrný kanál nestačí a jste zpátky u WebSocketu.
Kdy realtime sáhnout
Ve zkratce tři případy. Když se data mění zřídka a pár sekund zpoždění nevadí, stačí obyčejný dotaz nebo občasný polling. Když server jen posílá a klient poslouchá (přehledy, upozornění, průběh), sáhněte po SSE. A teprve tam, kde spolu obě strany mluví oběma směry a na zpoždění záleží – chat, spolupráce v jednom dokumentu, hry, komunitní a chatovací weby –, se vyplatí WebSocket; a pokud chcete fallback, znovupřipojení a skupiny bez ručního dopisování, socket.io nad ním.
Za tu pohodlnou vrstvu se platí: socket.io přenáší data ve vlastním formátu, takže na druhém konci musí být zase socket.io, ne holý WebSocket klient. Kdo potřebuje mluvit s cizí službou nebo si vystačí s jedním druhem zpráv, bývá s čistým WebSocketem jednodušší. Co si vybrat, je hlavně otázka toho, kolik z přidaných funkcí opravdu využijete.
Zdroje
Zdroje: dokumentace socket.io, oddíl How it works (rozdělení na Engine.IO a socket.io, tři přenosy a povyšování spojení), tatáž dokumentace k rooms (rooms jsou serverový pojem, join a io.to().emit()), MDN: WebSocket (události, readyState a chybějící backpressure), RFC 6455 (standard protokolu, prosinec 2011) a MDN: Using server-sent events (SSE je jednosměrné).