Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reacting with ReactPHP

Reacting with ReactPHP

A primer for getting an async socket server up and running with ReactPHP, and Websocket server with Ratchet. The code that goes with: https://github.com/catch404/dallasphp-201308

Bob Majdak Jr

August 13, 2013
Tweet

More Decks by Bob Majdak Jr

Other Decks in Programming

Transcript

  1. $loop = React\EventLoop\Factory::create(); // determines which event system is available.

    // libevent, libev, or stream_select, gives you back // an object that implements the loop interface. $loop->run(); // run the loop. // ... except our app does nothing... so...
  2. $loop = React\EventLoop\Factory::create(); // create a timer that executes every

    1 second. $loop->addPeriodicTimer(1,function(){ echo microtime(true), “ LOL”, PHP_EOL; }); $loop->run();
  3. > php bin\loop1.php 1376205167.2002 LOL 1376205168.2011 LOL 1376205169.2011 LOL 1376205170.202

    LOL 1376205171.2021 LOL 1376205172.2022 LOL 1376205173.2031 LOL ^C
  4. $loop->addTimer(int $time, callable $func); // run once after $time seconds.

    $loop->addPeriodicTimer(int $time, callable $func); // run every $time seconds. both return Timer objects with various methods, most useful of which may be the cancel() method. - see: src/React/EventLoop/Timer/Timer.php
  5. $loop = React\EventLoop\Factory::create(); $server = new React\Socket\Server($loop); // create an

    object that handles sockets. $server->listen(7890); // and have it listen on port 7890. $loop->run();
  6. $server = new React\Socket\Server($loop); $server->listen(7890); // bind a function to

    connection events so that the // socket server knows what to do about them. $server->on( ‘connection’, function($cx) { echo “a client has connected.”, PHP_EOL; });
  7. $server->on(‘connection’, function($cx) { // attach a function to the connection

    itself that // deals with handling any data it sends us. $cx->on(‘data’, function($input,$cx){ echo “data from client: {$input}”, PHP_EOL; }); });
  8. just because you received data, does not mean it is

    all your data. example: windows telnet sends every keypress as it happens. unix telnet waits until new line. messages could get broken up into multiple packets and arrive to the client in the wrong order. that is how the internet works. so as they arrive the NIC may choose to send you what it can construct properly *now* and wait until later to send the rest.
  9. solution: when you receive data, add it to a buffer.

    check the buffer each time to see if it has a complete message yet or not. super simple example: LF based protocols (fgets()) new line based protocol? does your data have a new line in it? start of buffer to the new line is a complete message.
  10. 6 8 9 1 2 3 4 7 5 oh

    hai guiz 6 8 9 7 5
  11. public function BufferAdd($input) { $this->Buffer .= $input; return; } public

    function BufferDrain() { while(($pos = strpos($this->Buffer,chr(10))) !== false) { $cmd = trim(substr($this->Buffer,0,$pos)); $this->Buffer = substr($this->Buffer,($pos+1)); if(!$cmd) continue; else return $cmd; } return null; }
  12. $cx->on(‘data’,function($input,$cx){ $cx->DataBuffer .= $input; while($msg = magic_line_reader($cx->DataBuffer)) { switch($msg) {

    case ‘ping’: $cx->write(“pong\n”); break; default: $cx->write(“error: unknown command\n”); break; } } });
  13. <?php $loop = React\EventLoop\Factory::create(); $server = new React\Socket\Server($loop); $server->listen(7890); $server->on(‘connection’,function($cx){

    $cx->on(‘data’,function($input,$cx) { ... }); $cx->on(‘close’,function($input,$cx) { ... }); }); $loop->run();
  14. class Server extends React\Socket\Server { protected $EventLoop; protevted $Port =

    7890; public function __construct() { $this->EventLoop = React\EventLoop\Factory::create(); parent::__construct($this->EventLoop); $this->listen($this->Port); $this->on('connection',[$this,'OnConnect']); return; }
  15. class Server extends React\Socket\Server { protected $EventLoop; public function Run()

    { return $this->EventLoop->run(); } public function OnConnect($cx) { // do stuffs } }
  16. <?php require(‘all-the-things’); $loop = React\EventLoop\Factory::create(); $server = new React\Socket\Server($loop); $server->listen(7890);

    $server->on(‘connection’,function($cx){ $cx->on(‘data’,function($input,$cx) { ... }); $cx->on(‘close’,function($input,$cx) { ... }); }); $loop->run();
  17. class Server implements MessageComponentInterface { public function onOpen() { }

    public function onClose() { } public function onError() { } public function onMessage() { } } Ratchet\Server\IoServer::factory( new Ratchet\WebSocket\WsServer(new Server), 7890 )->run();
  18. WEBSITE: i need this report SERVER: ok, come back in

    30 minutes. WEBSITE: ok thanks. SERVER: you there, you are not doing anything. generate this report. WORKER1: ok i’m off to work on it. bbl. // 30 min later WORKER1: all done. website can view it whenever. SERVER: ok thanks. i’ll email that bloke.
  19. class Server extends React\Socket\Server { public function __construct() { $this->EventLoop

    = React\EventLoop\Factory::create(); parent::__construct($this->EventLoop); $this->listen($this->Port); $this->on('connection',[$this,'OnConnect']); return; }
  20. class Client { public function __construct($host,$port) { $this->EventLoop = React\EventLoop\Factory::create();

    $fp = stream_socket_client("tcp://{$host}:{$port}"); if(!$fp) throw new \Exception(“unable to connect {$host}:{$port}”); stream_set_blocking($fp,false); $this->Socket = new React\Socket\Connection($fp,$this->EventLoop); $this->Socket->on('data',[$this,'OnDataIn']); $this->Socket->on('close',[$this,'OnDisconnect']); return; }
  21. everything else about clients is basically the same as servers.

    servers need to know how to handle: connections, data inputs, disconnections. clients need to know how to handle: data inputs, disconnections. both need to be able to: send data to the other.
  22. overview • EventLoops • React\Socket\Server • connection, error • React\Socket\Connection

    • data, close, error • Ratchet\WsServer • Class implementing MessageComponentInterface • open, close, error, message
  23. the end Bob Majdak Jr • @bobmajdakjr • irc: freenode

    #dallasphp • http://reactphp.org • http://socketo.me