Websocket Ratchet

В материале о websocket мы рассмотрели построение вебсокет-клиента на JavaScript. В качестве Websocket-сервера использовался сторонний эхо-сервер. Для создания собственного WebSocket сервера, нам понадобиться решение на PHP или JavaScript (или на С++, Java, Scala и т.д.)

На работающей сборке OpenServer (можно использовать WAMP или др. сборку), создадим хост websocket.host и перезапустим сборку для обновления файла hosts (C:/Windows/System32/Drivers/etc/hosts), чтобы в браузер «увидел» наш сайт.

Познакомимся с Ratchet, решением для организации работы с вебсокетами на PHP.

Создание WebSocket-сервера на Ratchet

Для установки Ratchet нам понадобиться установленный Composer. Если вы не знакомы с Composer, срочно восполните этот пробел: перейдите по адресу https://getcomposer.org/download/, выполните команды начинающиеся с «php», а потом пропишите адрес к утилите в системной переменной PATH. Или скачайте composer.phar Создайте файл composer.json внутри папки websocket.host и заполните его зависимостями, как указано тут:

{
  "autoload": {
    "psr-0": {
      "MyApp": "src"
    }
  },
  "require": {
    "cboden/ratchet": "0.3.*"
  }
}

После установки Composer и описания файла .json, нужно запустить установку Ratchet через командную строку: php composer.phar install

В результате появится папка vendor с необходимыми компонентами/библиотеками. Если интересно, можно посмотреть подробней об автозагрузке классов в PHP и работе с  пространствами имён в PHP

В корне сайта создадим src/MyApp/Chat.php:

<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

 
class Chat implements MessageComponentInterface {

public function onOpen(ConnectionInterface $conn) {    }

public function onMessage(ConnectionInterface $from, $msg) {    }

public function onClose(ConnectionInterface $conn) {    }

public function onError(ConnectionInterface $conn, \Exception $e){ }
}

И в корне создадим chat-server.php:


<?php
use Ratchet\Server\IoServer;
use MyApp\Chat;
require '/vendor/autoload.php';

$server = IoServer::factory(
  new Chat(),
  8080
);
$server->run();

Сам же чат обновим кодом:

<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
 
class Chat implements MessageComponentInterface {
protected $clients;

public function __construct() {
  $this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
  // Store the new connection to send messages to later
  $this->clients->attach($conn);
  echo "New connection! ({$conn->resourceId})\n";
}

public function onMessage(ConnectionInterface $from, $msg) {
  $numRecv = count($this->clients) - 1;
  echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');

foreach ($this->clients as $client) {
  if ($from !== $client) {
  // The sender is not the receiver, send to each client connected
  $client->send($msg);
  }
}
}

public function onClose(ConnectionInterface $conn) {
  // The connection is closed, remove it, as we can no longer send it messages
  $this->clients->detach($conn);
  echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
  echo "An error has occurred: {$e->getMessage()}\n";
  $conn->close();
}
}

При запуске в консоли  «php char-server.php» консоль будет «висеть» — сервер работает.

Остановим сервер Ctrl+C и изменим файл сервера:

<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;

 
require  '/vendor/autoload.php';
 

$server = IoServer::factory(
  new HttpServer(
    new WsServer(
      new Chat()
    )
  ),
 8080
);

$server->run();

Настало время проверить работу. Поменяем адрес websocket сервера:

var socket = new WebSocket("ws://localhost:8080");

Запустим php char-server.php и в форме внесём тестовые данные. В консольном окне мы должны увидеть приходящие сообщения. Если запустить вторую вкладку браузера, то станет возможным переписываться между двумя вкладками, одновременно видеть переписку в виде строк с JSON в консоли!

Чтобы сообщения накапливались подобно чату, нужно поменять строку в методе onmessage:

status.innerHTML += `пришли данные: <b>${message.name}</b>: ${message.msg}<br>`;

Если вы захотите сделать так, чтобы и отправитель тоже видел свои сообщения, нужно будет закомментировать фрагмент кода Chat.php:

foreach ($this->clients as $client) {

// if ($from !== $client) {
// Отправитель не является получателем, отправляем каждому подключенному клиенту
  $client->send($msg);
// }
}

Поздравляю! У нас есть работающий Websocket Ratchet сервер!