The WebSocket protocol allows a bidirectional and full-duplex communication between a client and a server. The `Hoa\Websocket` library allows to create WebSocket servers and clients.

## Table of contents

## Introduction

The WebSocket protocol is standardized in the RFC6455. It allows a client and a server to communicate between each other. This communication is bidirectional, it means that the client can send messages to the server, and vice versa. The server does not only send responses, it is able to send messages spontaneously. This changes the classical Web approach with its HTTP protocol. The WebSocket protocol is also full-duplex, it means that data are exchanged simultaneously in both 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 interactivity between both client and server. The client will often be a browser. It is important to notice that the URI schema (see the RFC3986) of the WebSocket is `ws://`.

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 HTTP protocol and provides only a unidirectional 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 the `Hoa\Eventsource` library).

The WebSocket protocol starts by a handshake in order to start the communication and exchange messages formatted as frames.

### Handshake and challenge

To start 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 challenge, kind of enigma, that the server must solve. If it succeed, the communication will start.

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 secured connection thought TLS. In this case, we will use the `wss://` URI schema.

### Frame and opcode

Messages between the client and the server are not exchanged verbatim. Actually, the message is formatted as a frame: A packet 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 section 5.2, Base Framing Protocol from the protocol specification for the most curious of you.

The type of the message is called opcode. This is the most important information. We will find this term in the chapter many times. The `Hoa\Websocket\Connection` class provides constants to represent each opcode in order to simplify their use. This class ensures the protocol support.

### History

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: draft-ietf-hybi-thewebsocketprotocol-00 (or draft-hixie-thewebsocketprotocol-76), or Hybi00 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.

The `Hoa\Websocket` library supports both versions. It allows clients supporting different versions of the protocol to communicate between each other.

## Write a server

The `Hoa\Websocket\Server` class allows to write a server manipulating the WebSocket protocol. This class inherits from `Hoa\Websocket\Connection`. The communication is based on a socket server. We will use the `Hoa\Socket\Server` class (from the `Hoa\Socket` library) to fill this role.

The WebSocket protocol runs on TCP, thus we will start a local WebSocket server on the 8889 port:

    $server = new Hoa\Websocket\Server(
        new Hoa\Socket\Server('tcp://127.0.0.1:8889')
    );

However, we can use the `ws://127.0.0.1:8889` URI directly instead of `tcp://127.0.0.1:8889`. This is an advantage when we use `wss://` for a secured connection because `Hoa\Websocket` 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:

    $server = new Hoa\Websocket\Server(
        new Hoa\Socket\Server('ws://127.0.0.1:8889')
    );

Now, let's see how to interact with this server.

### Listeners

The `Hoa\Websocket\Connection` class provides six listeners:

- `open`, when a connection is `opened`,
- `message`, when a message is received,
- `binary-message`, when a binary message is received,
- `ping`, when a ping is received,
- `error`, when an error has been triggered,
- `close`, when a connection is closing.

For the `message` and `binary-message` listeners, there is only one associated datum: `message`, which contains without any surprise the received `message`.

For the `ping` listener, we will also find the `message` datum. Notice that the pong is automatically sent before the listener is fired.

For the `error` listener, we will find the `exception` datum, which contains an exception (not necessary `Hoa\Websocket\Exception\Exception`, this can be for example `Hoa\Socket\Exception`). The listener is fired after that the connection has been closed.

The `close` listener has two data: `code` for the code and `reason` explains the reason of this closing with a short message. We will find standard closing codes through the `CLOSE_*` constants on the `Hoa\Websocket\Connection` class. For example, `Hoa\Websocket\Connection::CLOSE_NORMAL` represents a normal connection closing, without error, whereas `Hoa\Websocket\Connection::CLOSE_MESSAGE_ERROR` represents a connection closing because of a malformed message. This listener is fired after that the connection has been closed.

### Send messages

Let's complete our example to, in the `message` listener, send back to the client all received messages in order to create an echo. We will use the `Hoa\Websocket\Connection::send` method. Once our listener is registered, we can start the server thanks to the `Hoa\Websocket\Connection::run` method. Thus:

    $server->on('message', function (Hoa\Event\Bucket $bucket) {
        $data = $bucket->getData();

        echo 'message: ', $data['message'], "\n";
        $bucket->getSource()->send($data['message']);

        return;
    });

    $server->run();

Now, let's test our server by creating a very simple HTML client:

    &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>

At line 6, we declare the address of the WebSocket server by using the `ws` protocol. At line 45, we use the `WebSocket` object and we attach listeners, strongly similar to those of `Hoa\Websocket\Connection`!

To test, we just have to start the server:

    $ php Server.php

Then, open the client in your favorite browser. Each message sent to the server returns identically, we have an echo.

### Broadcast messages

So far, the client speaks with the server and the server replies, but this is only a dialog. However, the server has all connections in memory. We are able to broadcast a message to all connected clients. So, we will use the `Hoa\Websocket\Connection::broadcast` method that will send a message to all other connected clients. Thus:

    $server->on('message', function (Hoa\Event\Bucket $bucket) {
        $data = $bucket->getData();

        echo 'message: ', $data['message'], "\n";
        $bucket->getSource()->broadcast($data['message']);

        return;
    });

Et voilà ! Restart the server and open the client many times. Each message we send is broadcasted to all other clients! Our example is now an instant messaging tool.

We have to understand that the `Hoa\Socket\Server` socket server works with nodes, it means an object that represents an opened connection. In this listener, to know the current node that has fired it, we have to call the `Hoa\Websocket\Connection::getConnection` method to get the socket server, and then, the `Hoa\Socket\Server::getCurrentNode` method. Similarly, we have the `Hoa\Socket\Server::getNodes` to get a list of all nodes. The `Hoa\Websocket\Connection::broadcast` method is in fact implemented in the `Hoa\Socket` library and hides this complexity. It is preferable to use this method for performance and compatibility reasons.

### Closing

To close a connection with a client, we use the `Hoa\Websocket\Connection::close` method. This latter is very similar to `Hoa\Websocket\Connection::send`. Its arguments are:

- `code`: The closing code, see the `Hoa\Websocket\Connection::CLOSE_*` constants (`CLOSE_NORMAL` by default),
- `reason`: A short message explaining the reason of the closing (`null` by default),
- `node`: The node that will close the connection (`null`, by default, indicates the current node).

For example, when we will receive the message `I love you`, we will close the connection by explaining why, else we will only do an echo of the message:

    $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;
    });

We can modify our client in order to show the code and the reason of the closing:

          socket.onclose = function (e) {
              print(
                  'connection is closed (' + e.code + ' ' +
                  (e.reason || '—no reason—') + ')'
              );

              return;
          };

It is highly recommended to always use this method to close a connection instead of closing the TCP connection directly.

## Message

There is two ways to send a message: Either in one piece if we have the message entirely, or in several pieces. Our message can also contain other things than text, for example binary data. In this case, we speak about a binary message.

### Fragmentation

To send a message in one block, we use the `Hoa\Websocket\Connection::send` method as seen in previous sections:

    $server->on('message', function (Hoa\Event\Bucket $bucket) {
        $bucket->getSource()->send('foobar');

        return;
    });

In fact, this method has four arguments:

- `message`: The message,
- `node`: The node that will send the message (`null`, by default, indicates the current node),
- `opcode`: The opcode, it means the type of the frame (`Hoa\Websocket\Connection::OPCODE_TEXT_FRAME` by default),
- `fin`: Indicates whether the message is over or not (`true` by default).

We will use all these arguments by trying to send a fragmented message.

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 undetermined 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.

We will use the next opcodes: `OPCODE_TEXT_FRAME` for the first fragment, and then `OPCODE_CONTINUATION_FRAME` for the next ones. Each time, we will specify that the message is not over thanks to the `fin` argument which will be set to `false`, except for the last fragment where `fin` will be `true`.

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 reconstitute 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.

Let's see an example. We will send the `foobarbaz` 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:

    $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;
    });

The `sleep` instructions allow to emulate a network latency or something equivalent. Each time the `send` method is called, data are effectively sent to the client, there is no buffer on the server side.

### Encoding

All exchanged messages must be in the UTF-8 format (see the RFC3629). If a message from the client is not consistent with this format, `Hoa\Websocket\Connection` will close the connection appropriately with the `Hoa\Websocket\Connection::CLOSE_MESSAGE_ERROR` code, we have nothing special to do. Consequently, all the received messages in our listeners respect this encoding.

However, `Hoa\Websocket\Connection` verifies that messages going to the client are consistent with this format. If the encoding is not appropriated, then a `Hoa\Websocket\Exception\InvalidMessage` exception will be thrown, which will close the connection and fire the `error` listener if it is not captured earlier.

### Binary

It is also possible to send binary data, more compacted than textual data. We speak about binary messages. We will still use the `Hoa\Websocket\Connection::send` method but with the `OPCODE_BINARY_FRAME` opcode. This makes sense only in the `binary-message` 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):

    $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;
    });

The `pack` and `unpack` functions are precious friends here.

Notice that binary messages can also be fragmented. We have to use the `OPCODE_BINARY_FRAME` opcode instead of `OPCODE_TEXT_FRAME` and then continue with `OPCODE_CONTINUATION_FRAME` as we have learned.

## Write a client

The `Hoa\Websocket\Client` class allows to write a client manipulating the WebSocket protocol. This class inherits from `Hoa\Websocket\Connection`. The communication is based on a socket client. We will use the `Hoa\Socket\Client` class (from the `Hoa\Socket` library) to fill this role.

Much as `Hoa\Websocket\Server` is able to communicate with clients supporting different versions of the WebSocket protocol, `Hoa\Websocket\Client` uses only the RFC6455 protocol. It means that the client is only able to communicate with a server supporting the RFC6455 protocol.

### Start a connection with the server

Like the server, the client inherits from `Hoa\Websocket\Connection`. We find the same `send`, `close`, `run` and other methods, and also the same listeners. And like the server, listeners must be registered on the client.

Thus, to start a client, we will write:

    $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;
    });

The client can work in loop mode, like the server, with the `run` method. In this case, we will write:

    $client->run();

Or, for a sequential exchange, we have to manually call the `Hoa\Websocket\Client::connect` method:

    $client->connect();

If the server does not support the right protocol, a `Hoa\Websocket\Exception\BadProtocol` exception will be thrown.

### Send and broadcast messages

To send a message, we will use the `Hoa\Websocket\Connection::send` method. Its behavior has been described previously for the server. It is identical.

A contrario, to receive a message, we will use the `Hoa\Websocket\Client::receive` 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:

    $client->send('foobar');
    $client->receive();

    $client->send('bazqux');
    $client->receive();

    $client->close();

The `Hoa\Websocket\Client::receive` method has no argument.

### Note about the handshake and the host

In order the handshake to be complete, it is necessary to send a `Host` 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 `Hoa\Websocket\Client::setHost` method, before connecting the client (before the calls of `Hoa\Websocket\Connection::run` or `Hoa\Websocket\Client::connect`); thus:

    $client->setHost('hoa-project.net');
    $client->connect();

    // …

To know if the host is known, we can use the `Hoa\Websocket\Client::getHost` method. It will return `null` if the host is unknown. Or, during the handshake, an exception of type `Hoa\Websocket\Exception\Exception` will be thrown.

## Customized node

The `Hoa\Socket\Server` and `Hoa\Socket\Client` classes work with nodes: Objects representing an opened connection. The main class representing a node is `Hoa\Socket\Node`. The `Hoa\Websocket` library provides its own node: `Hoa\Websocket\Node`. We can extend this class in order to add and manipulate some information about a connection.

For example, in the case of an instant messaging, we could store the pseudo of the client:

    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;
        }
    }

The `Hoa\Socket\Server::setNodeName` or `Hoa\Socket\Client::setNodeName` methods allow to specify what node class the server or the client will use:

    $server = new Hoa\Websocket\Server(
        new Hoa\Socket\Server('ws://127.0.0.1:8889')
    );
    $server->getConnection()->setNodeName('ChatNode');

Next, in our listeners for example, we will be able to use our `getPseudo` method:

    $server->on('message', function (Hoa\Event\Bucket $bucket) {
        $node = $bucket->getSource()->getConnection()->getCurrentNode();

        var_dump($node->getPseudo());

        // …
    });

If you set up a protocol by using WebSockets between your clients and your server, customized nodes will be very helpful to store extra recurrent information.

## Conclusion

The `Hoa\Websocket` library allows to create WebSocket servers and clients for more interactivity in your applications. The server is easily extensible thanks to the notion of node which eases the storage and manipulation of useful data to create its own protocol.