WebSocketyOndřej Mirtes
Využitelné všude tam, kde teď máte periodické AJAX požadavky$chat, notifikace, hry, realtime updates…
Počáteční HTTP handshake, poté stálé spojení přes TCP.$V komunikaci se posílají jen změny (v AJAXu obvykle celý stav aplikace).
caniuse.com
Detekce podpory
return 'WebSocket' in window;
Nekontrolujte typ a verzi prohlížeče, ale vždy výskyt konkrétní funkcionality.
var ws = new WebSocket('ws://localhost:8080'); $ws.onopen = function() { $}; $ws.onmessage = function(event) { console.log(event.data);
}; $ws.onclose = function() { $};
Pozor - připojuje už konstruktor!
var ws = new WebSocketWrapper( 'ws://localhost:8080' ); ws.connect(); $ws.on('postLiked', function(data) { }); $ws.on('chatMessage', function(data) { $}); $ws.send({'action':'ping'});
https://gist.github.com/10647722
Můj vlastní wrapper, který řeší reconnecting a rozstřelování konkrétních akcí do jednotlivých callbacků (oproti jedinému "onmessage").
Zakažte uživateli provádět akce při
uzavřeném spojení
Hrozí ztráta dat.
Proč WebSockety v PHP a ne v něčem vhodnějším, třeba node.js?$$Pokud máte už běžící aplikaci, tak napsáním WS funkcionality v PHP budete těžit z jednotné codebase, využití znalostí týmu a stejných procesů na testování, continuous integration, build a deployment.$$Pokud stavíte na zelené louce a chcete se naučit něco nového, tak vás do PHP nutit nebudu :)
$loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $http = new React\Http\Server($socket, $loop); $$http-‐>on('request', function($request, $response) { $response-‐>writeHead( 200, ['Content-‐Type' => 'text/plain'] ); $response-‐>end("Hello World\n"); }); $$socket-‐>listen(1337); $loop-‐>run();
http://u.k47.cz/2Bw
while (true) { ... }
Event loop
http://u.k47.cz/2Bw
Request 1
Response 1
Request 2
Response 2
Request 3
Response 3
Request 4
Response 4
React je asynchronní, ale ne paralelní – běží stále v jednom vlákně.
while (true) { ... }
Event loop
http://u.k47.cz/2Bw
Response 1Response 2
Response 3
sleep(15);
Request 2 Request 3Request 1
composer.json{ "require": { "cboden/Ratchet": "~0.3" }, "suggest": { "ext-‐libevent": "" } }
S libevent rozšířením se použije efektivnější implementace event loop.
use Ratchet\Http\HttpServer; use Ratchet\Server\IoServer; use Ratchet\WebSocket\WsServer; use React\EventLoop\Factory; use React\Socket\Server; $$loop = Factory::create(); $server = new Server($loop); $server-‐>listen(8080, '0.0.0.0'); $new IoServer( new HttpServer(new WsServer($app)), $server ); $$loop-‐>run(); Spuštěný proces spravujte např. pomocí
supervisord.org, aby stále běžel.
use Ratchet\ConnectionInterface as Client; $class App implements \Ratchet\MessageComponentInterface { $ public function onOpen(Client $client) { $ } $ public function onMessage(Client $client, $message) { $ } $ public function onClose(Client $client) { } $ public function onError(Client $client, \Exception $e) { $ } $}
public function onMessage(Client $client, $message) { foreach ($this-‐>clients as $c) { if ($c !== $client) { $c-‐>send($message); } } }
Rozeslání zprávyna ostatní klienty
Při navázání spojení pošlete klientovi počáteční stav
Např. posledních 10 zpráv v chatu. Pokud byste počáteční stav nepotřebovali, tak vlastně nepotřebujete ani žádné úložiště na data.
Časovače
$loop-‐>addTimer(5, function() { // za pět sekund }); $$loop-‐>addPeriodicTimer(5, function() { // každých pět sekund });
Na odpojení uživatele při neaktivitě, na zasílání pingu pro udržení připojení. Časovače jsou jen v paměti, po pádu a obnovení procesu je musíte zrekonstruovat.
use Ratchet\Server\FlashPolicy; use Ratchet\Server\IoServer; use React\Socket\Server; $$server = new Server($loop); $server-‐>listen(843, '0.0.0.0'); $policy = new FlashPolicy(); $policy-‐>addAllowedAccess('*', 8080); $new IoServer($policy, $server); $loop-‐>run();
FlashPolicy
Long pollingAJAX fallback, který funguje všude$
POST požadavky pro odchozí zprávy$
Stream příchozích zpráv přes dlouhodobý GET požadavek$
xhr.responseText & xhr.onreadystatechange
Ratchet
Long polling server
WS
WS
HTTP
https://gist.github.com/10895929
Long polling server = HTTP serverv PHP + WebSocket klient v PHP :)
Ratchet neumí SSL (wss://)$
Webserver může fungovat jako proxy,která zabezpečenou komunikaci zajistí$
Webserver může WebSockety poskytnoutna klasických portech (80 a 443) – např. na subdoméně$
Apache – mod_proxy_wstunnel
Ratchet
ws.foo.com wslp.foo.com www.foo.com
Long polling
Nginx jako proxy může všechny služby poskytnout na klasických portech – 80, 443.
Webserver
CLI
Ratchet
WebSocket klienti
Ratchet běží v odděleném procesu, pokud potřebujete reagovat na akci z webové aplikace nebo třeba cronu, pošlete ji do Ratchetu pomocí ZMQ.
ZeroMQ - receiver
use React\ZMQ\Context; $$context = new Context($loop); $socket = $context-‐>getSocket(ZMQ::SOCKET_PULL); $socket-‐>bind('tcp://127.0.0.1:5555'); $socket-‐>on('message', [$app, 'onZmqMessage']);
ZeroMQ - sender
$context = new ZMQContext(); $socket = $context-‐>getSocket( ZMQ::SOCKET_PUSH, 'id' ); $socket-‐>connect('tcp://127.0.0.1:5555'); $socket-‐>send('ahoj!');
@OndrejMirtes