Блог экспериментатора инженера-разработчика: Infanty.
Я пишу how-to статьи на редкие темы или статьи обзоры - для себя и тех кто со мной работает.
Блог существует при поддержке: "Оккупационных сил Марса".

Ratchet позволяет разрабатывать приложения на PHP используя протокол полнодуплексной связи поверх TCP-соединения (WebSocket), предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени (позволяет одновременно передавать и принимать данные).

Ratchet построен на основе ReactPHP - сокет сервера разработанного на PHP для постоянной обработки запросов в отличии от стандартного подхода с Apache и Nginx где процесс умирает по окончании обработки одного запроса. Поскольку инициализация кода таким образом осуществляется только один раз то на отдельном запросе мы упускаем весь оверхед от загрузки классов, запуска фреймворка, считывания конфигурации и т.д.

Установка libev

1. Для начала установим libev, библиотеку асинхронного неблокирующего ввода / вывода, набрав в консоли:

sudo apt-get install libev-dev 

2. Скачаем архив расширения PHP для работы с библиотекой libev, по интернет адресу: https://github.com/m4rw3r/php-libev. После чего - скопируем его на сервер, например в папку: "/var/www/php-libev".

3. Перейдём в папку "/var/www/php-libev" и наберём в консоли:

phpize
./configure --with-libev
make
make install 

4. Добавим "extension=libev.so" в php.ini для подключения расширения PHP, после чего перезапустим PHP набрав в консоли (в данном варианте работает связка apache2 и PHP):

sudo service apache2 restart 

5. Проверим работу библиотеки с помощью скрипта "/var/www/php-libev/test.php" следующего содержания:

<?php
  error_reporting(E_ALL);
  ini_set("display_errors", 1);
 
  include_once '/var/www/php-libev/libev.php';
 
  $loop = new libev\EventLoop();

  $repeater = new libev\TimerEvent(function() {
    echo "I repeat every second!\n";
  }, 1, 1);

  $single = new libev\TimerEvent(function() {
    echo "I will fire after 5 seconds, without repeat\n";
  }, 5);

  $loop->add($repeater);
  $loop->add($single);

  $loop->run();
?> 

Пример выше запускается командой из консоли: "php -f /var/www/php-libev/test.php".

Установка Ratchet

6. В папке для приложений с использованием Ratchet, например: "/var/www/ratchet", создадим файл composer.json следующего содержания:

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

7. После чего запускаем следующие команды для установки Composer и установки с помощью него Ratchet:

curl -s https://getcomposer.org/installer | php
php composer.phar install 

8. Создадим приложение чата с помощью Ratchet, разместив следующий код в файле "/var/www/ratchet/src/MyApp/Chat.php":

<?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();
    }
  } 

Где:

  • OnOpen - Вызывается, когда подключился новый клиент.
  • OnMessage - Вызывается, когда сообщение получено с помощью Connection.
  • OnClose - Вызывается, когда Соединение закрыто.
  • OnError - Вызывается при возникновении ошибки на связи.

8. Так же на необходим сервер ввода / вывода который бы:

  • устанавливал соединение;
  • передавал данные - пересылаемые между клиентами нашего чата;
  • ловил ошибки;
  • инициализировал экземпляр приложения чата с использованием определённого порта.

Создадим для этого файл "/var/www/ratchet/bin/Chat-Server.php" следующего содержания:

<?php
  use Ratchet\Server\IoServer;
  use MyApp\Chat;

  require dirname(__DIR__) . '/vendor/autoload.php';

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

  $server->run(); 

9. Для тестирования работы чата достаточно запустить сервер ввода / вывода, командой в консоли:

php /var/www/ratchet/bin/chat-server.php 

После чего открыть две новые консоли и соединиться в каждой из них с чатом, командой:

telnet localhost 8080 

После чего Вы можете в одной из этих консолей набрать сообщение, а во второй увидеть его.

10. Дополнительно рекомендую ознакомится с материалами:

  • Как обратится к созданному приложению из JavaScript: https://www.sitepoint.com/how-to-quickly-build-a-chat-app-with-ratchet/.
  • Как расширить приложение для того, чтобы хранить лог сообщений: http://socketo.me/docs/push.
  • Как запускать приложение Ratchet используя Suporvisor, для автоматического перезапуска приложения в случае его недоступности: http://socketo.me/docs/deploy (глава: "Supervisor", раздел файла настройки: "[program:ratchet]"). Сам Suporvisor устанавливается командой: "sudo apt-get install supervisor", а добавляется в автозагрузку командой: "sudo update-rc.d supervisor defaults".