| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- <?xml version="1.0" encoding="utf-8"?>
-
- <overlay xmlns="http://hoa-project.net/xyl/xylophone">
- <yield id="chapter">
-
- <p>The WebSocket protocol allows a <strong>bidirectional</strong> and
- <strong>full-duplex</strong> communication between a client and a server. The
- <code>Hoa\Websocket</code> library allows to create WebSocket
- <strong>servers</strong> and <strong>clients</strong>.</p>
-
- <h2 id="Table_of_contents">Table of contents</h2>
-
- <tableofcontents id="main-toc" />
-
- <h2 id="Introduction" for="main-toc">Introduction</h2>
-
- <p>The WebSocket protocol is standardized in the
- <a href="https://tools.ietf.org/html/rfc6455">RFC6455</a>. It allows a client
- and a server to communicate between each other. This communication is
- <strong>bidirectional</strong>, it means that the client can send messages to
- the server, and <strong>vice versa</strong>. The server does not only send
- responses, it is able to send messages <strong>spontaneously</strong>. This
- changes the classical Web approach with its HTTP protocol. The WebSocket
- protocol is also <strong>full-duplex</strong>, it means that data are
- exchanged <strong>simultaneously</strong> in <strong>both</strong> directions:
- While a data is currently transiting from the server to the client, the latter
- is able to also send a message to the server. Consequently, the WebSocket
- protocol allows a high <strong>interactivity</strong> between both client and
- server. The client will often be a browser. It is important to notice that the
- URI schema (see the <a href="https://tools.ietf.org/html/rfc3986">RFC3986</a>)
- of the WebSocket is <code>ws://</code>.</p>
- <p>We can ask ourselves what are the differences between WebSocket and
- EventSource. Both solutions are fundamentaly differents: WebSocket allows a
- bidirectional and full-duplex communication, whereas EventSource is a
- technology based on the <strong>HTTP protocol</strong> and provides only a
- <strong>unidirectional</strong> communication. For this kind of usage, an
- EventSource server is more likely to be light, simple and it is designed to be
- robust regarding disconnections (see <a href="@hack:chapter=Eventsource">the
- <code>Hoa\Eventsource</code> library</a>).</p>
- <p>The WebSocket protocol starts by a handshake in order to start the
- communication and exchange messages formatted as frames.</p>
-
- <h3 id="Handshake_and_challenge" for="main-toc">Handshake and challenge</h3>
-
- <p>To <strong>start</strong> a communication with the WebSocket protocol, the
- client must send an HTTP request to the server asking to change the protocol.
- In this request, the client inserts a <strong>challenge</strong>, kind of
- enigma, that the server must solve. If it succeed, the communication will
- start.</p>
- <p>Starting by an HTTP request is not anodyne. It allows the WebSocket
- protocol to use the same network path as HTTP requests, and so, for example,
- traverse proxys, firewalls etc. This also eases the deployment of this
- protocol: No need to open a dedicated port, no need to have a specific
- configuration on the server etc. Finally, it allows to use a
- <strong>secured</strong> connection thought TLS. In this case, we will use the
- <code>wss://</code> URI schema.</p>
-
- <h3 id="Frame_and_opcode" for="main-toc">Frame and opcode</h3>
-
- <p>Messages between the client and the server are not exchanged verbatim.
- Actually, the message is <strong>formatted</strong> as a frame: A
- <strong>packet</strong> of bits with a specific design. In this case, we will
- find information about the type of the message, its length, some
- verification codes etc. We will find an explanatory schema in the
- <a href="https://tools.ietf.org/html/rfc6455#section-5.2">section 5.2, Base
- Framing Protocol</a> from the protocol specification for the most curious of
- you.</p>
- <p>The <strong>type</strong> of the message is called opcode. This is the most
- important information. We will find this term in the chapter many times. The
- <code>Hoa\Websocket\Connection</code> class provides constants to represent
- each opcode in order to simplify their use. This class ensures the protocol
- <strong>support</strong>.</p>
-
- <h3 id="History" for="main-toc">History</h3>
-
- <p>There is two versions of the WebSocket protocol in the nature: The standard
- version and the non-standard version. The standard version is the one
- described in the RFC6455. The last non-standard version has the following
- nickname: <em>draft-ietf-hybi-thewebsocketprotocol-00</em> (or
- <em>draft-hixie-thewebsocketprotocol-76</em>), or
- <a href="https://tools.ietf.org/wg/hybi/draft-ietf-hybi-thewebsocketprotocol/">Hybi00</a>
- for short. This non-standard version has many important security issues but it
- is in use in some languages such as Flash. Fortunately, this version
- disappears more and more and is replaced by the RFC6455.</p>
- <p>The <code>Hoa\Websocket</code> library supports both versions. It allows
- clients supporting different versions of the protocol to communicate between
- each other.</p>
-
- <h2 id="Write_a_server" for="main-toc">Write a server</h2>
-
- <p>The <code>Hoa\Websocket\Server</code> class allows to write a server
- <strong>manipulating</strong> the WebSocket protocol. This class inherits
- from <code>Hoa\Websocket\Connection</code>. The <strong>communication</strong>
- is based on a socket server. We will use the <code>Hoa\Socket\Server</code>
- class (from <a href="@hack:chapter=Socket">the <code>Hoa\Socket</code>
- library</a>) to fill this role.</p>
- <p>The WebSocket protocol runs on TCP, thus we will start a local WebSocket
- server on the 8889 port:</p>
- <pre><code class="language-php">$server = new Hoa\Websocket\Server(
- new Hoa\Socket\Server('tcp://127.0.0.1:8889')
- );</code></pre>
- <p>However, we can use the <code>ws://127.0.0.1:8889</code> URI directly
- instead of <code>tcp://127.0.0.1:8889</code>. This is an advantage when we
- use <code>wss://</code> for a secured connection because
- <code>Hoa\Websocket</code> will know that the connection will need to be
- secured and everything will be done for you automatically. Manipulating TLS,
- enabling encryption for certain connections etc. will not be required.
- Thus:</p>
- <pre><code class="language-php">$server = new Hoa\Websocket\Server(
- new Hoa\Socket\Server('ws://127.0.0.1:8889')
- );</code></pre>
-
- <p>Now, let's see how to <strong>interact</strong> with this server.</p>
-
- <h3 id="Listeners" for="main-toc">Listeners</h3>
-
- <p>The <code>Hoa\Websocket\Connection</code> class provides six listeners:</p>
- <ul>
- <li><code>open</code>, when a connection is <code>opened</code>,</li>
- <li><code>message</code>, when a <strong>message</strong> is received,</li>
- <li><code>binary-message</code>, when a <strong>binary</strong> message is
- received,</li>
- <li><code>ping</code>, when a <strong>ping</strong> is received,</li>
- <li><code>error</code>, when an <strong>error</strong> has been
- triggered,</li>
- <li><code>close</code>, when a connection is <strong>closing</strong>.</li>
- </ul>
- <p>For the <code>message</code> and <code>binary-message</code> listeners,
- there is only one associated datum: <code>message</code>, which contains
- without any surprise the received <code>message</code>.</p>
- <p>For the <code>ping</code> listener, we will also find the
- <code>message</code> datum. Notice that the pong is
- <strong>automatically</strong> sent before the listener is fired.</p>
- <p>For the <code>error</code> listener, we will find the
- <code>exception</code> datum, which contains an <strong>exception</strong>
- (not necessary <code>Hoa\Websocket\Exception\Exception</code>, this can be for
- example <code>Hoa\Socket\Exception</code>). The listener is fired after that
- the connection has been closed.</p>
- <p>The <code>close</code> listener has two data: <code>code</code> for the
- <strong>code</strong> and <code>reason</code> explains the
- <strong>reason</strong> of this closing with a short message. We will find
- standard closing codes through the <code>CLOSE_<em>*</em></code> constants on
- the <code>Hoa\Websocket\Connection</code> class. For example,
- <code>Hoa\Websocket\Connection::CLOSE_NORMAL</code> represents a normal
- connection closing, without error, whereas
- <code>Hoa\Websocket\Connection::CLOSE_MESSAGE_ERROR</code> represents a
- connection closing because of a malformed message. This listener is fired
- after that the connection has been closed.</p>
-
- <h3 id="Send_messages" for="main-toc">Send messages</h3>
-
- <p>Let's complete our example to, in the <code>message</code> listener,
- <strong>send back</strong> to the client all received messages in order to
- create an <strong>echo</strong>. We will use the
- <code>Hoa\Websocket\Connection::send</code> method. Once our listener is
- registered, we can start the server thanks to the
- <code>Hoa\Websocket\Connection::run</code> method. Thus:</p>
- <pre data-line="5"><code class="language-php">$server->on('message', function (Hoa\Event\Bucket $bucket) {
- $data = $bucket->getData();
-
- echo 'message: ', $data['message'], "\n";
- $bucket->getSource()->send($data['message']);
-
- return;
- });
-
- $server->run();</code></pre>
- <p>Now, let's test our server by creating a very simple HTML client:</p>
- <pre data-line="6,39"><code class="language-markup">&lt;input type="text" id="input" placeholder="Message…" />
- &lt;hr />
- &lt;pre id="output">&lt;/pre>
-
- &lt;script>
- var host = 'ws://127.0.0.1:8889';
- var socket = null;
- var input = document.getElementById('input');
- var output = document.getElementById('output');
- var print = function (message) {
- var samp = document.createElement('samp');
- samp.innerHTML = message + '\n';
- output.appendChild(samp);
-
- return;
- };
-
- input.addEventListener('keyup', function (evt) {
- if (13 === evt.keyCode) {
- var msg = input.value;
-
- if (!msg) {
- return;
- }
-
- try {
- socket.send(msg);
- input.value = '';
- input.focus();
- } catch (e) {
- console.log(e);
- }
-
- return;
- }
- });
-
- try {
- socket = new WebSocket(host);
- socket.onopen = function () {
- print('connection is opened');
- input.focus();
-
- return;
- };
- socket.onmessage = function (msg) {
- print(msg.data);
-
- return;
- };
- socket.onclose = function () {
- print('connection is closed');
-
- return;
- };
- } catch (e) {
- console.log(e);
- }
- &lt;/script></code></pre>
- <p>At line 6, we declare the address of the WebSocket server by using the
- <code>ws</code> protocol. At line 45, we use the
- <a href="https://developer.mozilla.org/docs/WebSockets/WebSockets_reference/WebSocket"><code>WebSocket</code>
- object</a> and we attach listeners, strongly similar to those of
- <code>Hoa\Websocket\Connection</code>!</p>
- <p>To test, we just have to start the server:</p>
- <pre><code class="language-shell">$ php Server.php</code></pre>
- <p>Then, open the client in your favorite browser. Each message sent to the
- server returns identically, we have an echo.</p>
-
- <h3 id="Broadcast_messages" for="main-toc">Broadcast messages</h3>
-
- <p>So far, the client speaks with the server and the server replies, but this
- is only a <strong>dialog</strong>. However, the server has all connections in
- memory. We are able to <strong>broadcast</strong> a message to all connected
- clients. So, we will use the <code>Hoa\Websocket\Connection::broadcast</code>
- method that will send a message to all other connected clients. Thus:</p>
- <pre data-line="5"><code class="language-php">$server->on('message', function (Hoa\Event\Bucket $bucket) {
- $data = $bucket->getData();
-
- echo 'message: ', $data['message'], "\n";
- $bucket->getSource()->broadcast($data['message']);
-
- return;
- });</code></pre>
- <p><em lang="fr">Et voilà !</em> Restart the server and open the client many
- times. Each message we send is broadcasted to <strong>all</strong> other
- clients! Our example is now an <strong>instant messaging</strong> tool.</p>
- <p>We have to understand that the <code>Hoa\Socket\Server</code> socket server
- works with <strong>nodes</strong>, it means an object that represents an
- opened connection. In this listener, to know the <strong>current</strong> node
- that has fired it, we have to call the
- <code>Hoa\Websocket\Connection::getConnection</code> method to get the socket
- server, and then, the <code>Hoa\Socket\Server::getCurrentNode</code> method.
- Similarly, we have the <code>Hoa\Socket\Server::getNodes</code> to get a list
- of all nodes. The <code>Hoa\Websocket\Connection::broadcast</code> method is
- in fact implemented in the <code>Hoa\Socket</code> library and hides this
- complexity. It is preferable to use this method for
- <strong>performance</strong> and compatibility reasons.</p>
-
- <h3 id="Closing" for="main-toc">Closing</h3>
-
- <p>To <strong>close</strong> a connection with a client, we use the
- <code>Hoa\Websocket\Connection::close</code> method. This latter is very
- similar to <code>Hoa\Websocket\Connection::send</code>. Its arguments are:</p>
- <ul>
- <li><code>code</code>: The closing <strong>code</strong>, see the
- <code>Hoa\Websocket\Connection::CLOSE_<em>*</em></code> constants
- (<code>CLOSE_NORMAL</code> by default),</li>
- <li><code>reason</code>: A short message explaining the
- <strong>reason</strong> of the closing (<code>null</code> by default),</li>
- <li><code>node</code>: The <strong>node</strong> that will close the
- connection (<code>null</code>, by default, indicates the current node).</li>
- </ul>
- <p>For example, when we will receive the message <code>I love you</code>, we
- will close the connection by explaining why, else we will only do an echo of
- the message:</p>
- <pre><code class="language-php">$server->on('message', function (Hoa\Event\Bucket $bucket) {
- $data = $bucket->getData();
-
- if ('I love you' === $data['message']) {
- $bucket->getSource()->close(
- Hoa\Websocket\Connection::CLOSE_NORMAL,
- 'Thank you but my heart is already taken, bye bye!'
- );
-
- return;
- }
-
- $bucket->getSource()->send($data['message']);
-
- return;
- });</code></pre>
- <p>We can modify our client in order to show the code and the reason of the
- closing:</p>
- <pre><code class="language-javascript"> socket.onclose = function (e) {
- print(
- 'connection is closed (' + e.code + ' ' +
- (e.reason || '—no reason—') + ')'
- );
-
- return;
- };</code></pre>
- <p>It is highly recommended to <strong>always</strong> use this method to
- close a connection instead of closing the TCP connection directly.</p>
-
- <h2 id="Message" for="main-toc">Message</h2>
-
- <p>There is two ways to send a message: Either in one piece if we have the
- message <strong>entirely</strong>, or in <strong>several</strong> pieces. Our
- message can also contain other things than text, for example
- <strong>binary</strong> data. In this case, we speak about a binary
- message.</p>
-
- <h3 id="Fragmentation" for="main-toc">Fragmentation</h3>
-
- <p>To send a message in one block, we use the
- <code>Hoa\Websocket\Connection::send</code> method as seen in previous
- sections:</p>
- <pre><code class="language-php">$server->on('message', function (Hoa\Event\Bucket $bucket) {
- $bucket->getSource()->send('foobar');
-
- return;
- });</code></pre>
- <p>In fact, this method has four arguments:</p>
- <ul>
- <li><code>message</code>: The <strong>message</strong>,</li>
- <li><code>node</code>: The <strong>node</strong> that will send the message
- (<code>null</code>, by default, indicates the current node),</li>
- <li><code>opcode</code>: The <strong>opcode</strong>, it means the type of
- the frame (<code>Hoa\Websocket\Connection::OPCODE_TEXT_FRAME</code> by
- default),</li>
- <li><code>fin</code>: Indicates whether the message is <strong>over</strong>
- or not (<code>true</code> by default).</li>
- </ul>
- <p>We will use all these arguments by trying to send a
- <strong>fragmented</strong> message.</p>
- <p>In our example, we have sent a message entirely, in one piece, which is the
- most usual way to do. If we send a very long message, we will also use this
- method. However, it is possible that we have the message piece by piece, and
- then, we are unable to send it entirely. For example, if a message, with an
- <strong>undetermined</strong> length, is read on a stream, and we would
- like to send it to a client, we won't wait to get the full message: We will
- send each piece directly to the client. In this case, we speak about
- fragmented messages.</p>
- <p>We will use the next opcodes: <code>OPCODE_TEXT_FRAME</code> for the
- <strong>first</strong> fragment, and then
- <code>OPCODE_CONTINUATION_FRAME</code> for the <strong>next ones</strong>.
- Each time, we will specify that the message is not <strong>over</strong>
- thanks to the <code>fin</code> argument which will be set to
- <code>false</code>, except for the last fragment where <code>fin</code> will
- be <code>true</code>.</p>
- <p>The final user behind the client won't receive fragmented messages, but one
- message in one block once the last fragment will be received. From the server
- side, this will avoid to overload the memory with “transit” data, and also to
- overload the network with a heavy message. We send data as soon as we get
- them, and it is part to the client to <strong>reconstitute</strong> the
- message. The server acts in the same way when it receives a fragment message.
- Between both fragments, the server is able to compute other tasks. So, it is
- more interesting to use fragments instead of bufferizing the message.</p>
- <p>Let's see an example. We will send the <code>foobarbaz</code> fragmented
- message in three pieces. We can imagine we read these data on a socket for
- example, and that the data are coming as soon as they are available. Thus:</p>
- <pre><code class="language-php">$server->on('message', function (Hoa\Event\Bucket $bucket) {
- $self = $bucket->getSource();
-
- $self->send(
- 'foo',
- null,
- Hoa\Websocket\Connection::OPCODE_TEXT_FRAME,
- false // not the end…
- );
- echo 'sent foo', "\n";
- sleep(1);
-
- $self->send(
- 'bar',
- null,
- Hoa\Websocket\Connection::OPCODE_CONTINUATION_FRAME,
- false // not the end…
- );
- echo 'sent bar', "\n";
- sleep(1);
-
- $self->send(
- 'baz',
- null,
- Hoa\Websocket\Connection::OPCODE_CONTINUATION_FRAME,
- true // the end!
- );
- echo 'sent baz, over', "\n";
-
- return;
- });</code></pre>
- <p>The <a href="http://php.net/sleep"><code>sleep</code></a> instructions
- allow to emulate a network latency or something equivalent. Each time the
- <code>send</code> method is called, data are <strong>effectively</strong> sent
- to the client, there is no buffer on the server side.</p>
-
- <h3 id="Encoding" for="main-toc">Encoding</h3>
-
- <p>All exchanged messages must be in the <strong>UTF-8</strong> format (see
- the <a href="https://tools.ietf.org/html/rfc3629">RFC3629</a>). If a message
- from the client is not consistent with this format,
- <code>Hoa\Websocket\Connection</code> will close the connection appropriately
- with the <code>Hoa\Websocket\Connection::CLOSE_MESSAGE_ERROR</code> code, we
- have nothing special to do. Consequently, all the received messages in our
- listeners respect this encoding.</p>
- <p>However, <code>Hoa\Websocket\Connection</code> verifies that messages
- <strong>going to</strong> the client are consistent with this format. If the
- encoding is not appropriated, then a
- <code>Hoa\Websocket\Exception\InvalidMessage</code> exception will be thrown,
- which will close the connection and fire the <code>error</code> listener if it
- is not captured earlier.</p>
-
- <h3 id="Binary" for="main-toc">Binary</h3>
-
- <p>It is also possible to send <strong>binary</strong> data, more compacted
- than textual data. We speak about binary messages. We will still use the
- <code>Hoa\Websocket\Connection::send</code> method but with the
- <code>OPCODE_BINARY_FRAME</code> opcode. This makes sense only in the
- <code>binary-message</code> listener, it means in a “binary communication”
- between the client and the server. We can imagine the client sending
- coordinates and the server giving others (for example for a board game):</p>
- <pre><code class="language-php">$server->on('binary-message', function (Hoa\Event\Bucket $bucket) {
- $data = $bucket->getData();
- $message = $data['message'];
- $point = [];
- list($point['x'], $point['y']) = array_values(unpack('nx/ny', $message));
-
- // compute a next point.
-
- $bucket->getSource()->send(
- pack('nn', $point['x'], $point['y']),
- null,
- Hoa\Websocket\Connection::OPCODE_BINARY_FRAME
- );
-
- return;
- });</code></pre>
- <p>The <a href="http://php.net/pack"><code>pack</code></a> and
- <a href="http://php.net/unpack"><code>unpack</code></a> functions are
- precious friends here.</p>
- <p>Notice that binary messages can also be <strong>fragmented</strong>. We
- have to use the <code>OPCODE_BINARY_FRAME</code> opcode instead of
- <code>OPCODE_TEXT_FRAME</code> and then continue with
- <code>OPCODE_CONTINUATION_FRAME</code> as we have learned.</p>
-
- <h2 id="Write_a_client" for="main-toc">Write a client</h2>
-
- <p>The <code>Hoa\Websocket\Client</code> class allows to write a client
- <strong>manipulating</strong> the WebSocket protocol. This class inherits from
- <code>Hoa\Websocket\Connection</code>. The <strong>communication</strong> is
- based on a socket client. We will use the <code>Hoa\Socket\Client</code> class
- (from <a href="@hack:chapter=Socket">the <code>Hoa\Socket</code> library</a>)
- to fill this role.</p>
- <p>Much as <code>Hoa\Websocket\Server</code> is able to communicate with
- clients supporting different versions of the WebSocket protocol,
- <code>Hoa\Websocket\Client</code> uses <strong>only</strong> the RFC6455
- protocol. It means that the client is only able to communicate with a server
- supporting the RFC6455 protocol.</p>
-
- <h3 id="Start_a_connection_with_the_server" for="main-toc">Start a connection
- with the server</h3>
-
- <p>Like the server, the client inherits from
- <code>Hoa\Websocket\Connection</code>. We find the same <code>send</code>,
- <code>close</code>, <code>run</code> and other methods, and also the same
- listeners. And like the server, listeners must be registered on the
- client.</p>
- <p>Thus, to start a client, we will write:</p>
- <pre><code class="language-php">$client = new Hoa\Websocket\Client(
- new Hoa\Socket\Client('ws://127.0.0.1:8889')
- );
- $client->on('message', function (Hoa\Event\Bucket $bucket) {
- $data = $bucket->getData();
- echo 'received message: ', $data['message'], "\n";
-
- return;
- });</code></pre>
- <p>The client can work in loop mode, like the server, with the
- <code>run</code> method. In this case, we will write:</p>
- <pre><code class="language-php">$client->run();</code></pre>
- <p>Or, for a sequential exchange, we have to manually call the
- <code>Hoa\Websocket\Client::connect</code> method:</p>
- <pre><code class="language-php">$client->connect();</code></pre>
- <p>If the server does not support the right protocol, a
- <code>Hoa\Websocket\Exception\BadProtocol</code> exception will be thrown.</p>
-
- <h3 id="Send_and_broadcast_messages" for="main-toc">Send and broadcast
- messages</h3>
-
- <p>To send a message, we will use the
- <code>Hoa\Websocket\Connection::send</code> method. Its behavior has been
- described previously for the server. It is identical.</p>
- <p><em>A contrario</em>, to receive a message, we will use the
- <code>Hoa\Websocket\Client::receive</code> method. The received messages from
- the server will fire listeners. Thus, we will send a message to the server,
- and then we will wait a response, twice:</p>
- <pre><code class="language-php">$client->send('foobar');
- $client->receive();
-
- $client->send('bazqux');
- $client->receive();
-
- $client->close();</code></pre>
- <p>The <code>Hoa\Websocket\Client::receive</code> method has no argument.</p>
-
- <h3 id="Note_about_the_handshake_and_the_host" for="main-toc">Note about the
- handshake and the host</h3>
-
- <p>In order the handshake to be complete, it is necessary to send a
- <code>Host</code> HTTP header, representing the name of the host. When the
- client is executed through an HTTP server, the host of the server will be used
- if available. Else, if it is not available, or if we execute the client in a
- command line for example, we have to specify a host with the
- <code>Hoa\Websocket\Client::setHost</code> method, before connecting the
- client (before the calls of <code>Hoa\Websocket\Connection::run</code> or
- <code>Hoa\Websocket\Client::connect</code>); thus:</p>
- <pre><code class="language-php">$client->setHost('hoa-project.net');
- $client->connect();
-
- // …</code></pre>
- <p>To know if the host is known, we can use the
- <code>Hoa\Websocket\Client::getHost</code> method. It will return
- <code>null</code> if the host is unknown. Or, during the handshake, an
- exception of type <code>Hoa\Websocket\Exception\Exception</code> will be
- thrown.</p>
-
- <h2 id="Customized_node" for="main-toc">Customized node</h2>
-
- <p>The <code>Hoa\Socket\Server</code> and <code>Hoa\Socket\Client</code>
- classes work with <strong>nodes</strong>: Objects representing an opened
- <strong>connection</strong>. The main class representing a node is
- <code>Hoa\Socket\Node</code>. The <code>Hoa\Websocket</code> library provides
- its own node: <code>Hoa\Websocket\Node</code>. We can <strong>extend</strong>
- this class in order to add and manipulate some <strong>information</strong>
- about a connection.</p>
- <p>For example, in the case of an instant messaging, we could store the
- pseudo of the client:</p>
- <pre><code class="language-php">class ChatNode extends Hoa\Websocket\Node
- {
- protected $_pseudo = null;
-
- public function setPseudo ($pseudo)
- {
- $old = $this->_pseudo;
- $this->_pseudo = $pseudo;
-
- return $old;
- }
-
- public function getPseudo()
- {
- return $this->_pseudo;
- }
- }</code></pre>
- <p>The <code>Hoa\Socket\Server::setNodeName</code> or
- <code>Hoa\Socket\Client::setNodeName</code> methods allow to specify what
- node class the server or the client will <strong>use</strong>:</p>
- <pre><code class="language-php">$server = new Hoa\Websocket\Server(
- new Hoa\Socket\Server('ws://127.0.0.1:8889')
- );
- $server->getConnection()->setNodeName('ChatNode');</code></pre>
- <p>Next, in our listeners for example, we will be able to use our
- <code>getPseudo</code> method:</p>
- <pre><code class="language-php">$server->on('message', function (Hoa\Event\Bucket $bucket) {
- $node = $bucket->getSource()->getConnection()->getCurrentNode();
-
- var_dump($node->getPseudo());
-
- // …
- });</code></pre>
- <p>If you set up a <strong>protocol</strong> by using WebSockets between
- your clients and your server, customized nodes will be very helpful to store
- extra recurrent information.</p>
-
- <h2 id="Conclusion" for="main-toc">Conclusion</h2>
-
- <p>The <code>Hoa\Websocket</code> library allows to create WebSocket
- <strong>servers</strong> and <strong>clients</strong> for more
- <strong>interactivity</strong> in your applications. The server is easily
- extensible thanks to the notion of <strong>node</strong> which eases the
- storage and manipulation of useful data to create its own protocol.</p>
-
- </yield>
- </overlay>
|