В этой статье я распишу по шагам как сделать простое приложение использующее вебсокеты, с применением сервера Centrifugo и отправкой сообщений из PHP.
Что такое вебсокет
Вебсокет — это универсальный механизм для общения по сети двух программ.
Сервер открывает соединение на свой IP и какой-то порт TCP. Клиент подключается по этому адресу и порту, и начинает общаться с сервером. Сообщения можно отправлять и принимать в обе стороны. По окончанию работы клиент закрывает соединение.
Для чего применяется вебсокет
В веб-разработке вебсокеты применяются там, где нужно больше скорости или гибкости в сетевом взаимодействии по сравнению с классическими запросами HTTP. Низкие расходы на доставку сообщений ещё и экономят трафик.
Разрабатывать приложения на вебсокетах немного сложнее, поэтому вебсокеты обычно применяют там где они дают ощутимые преимущества, в определённых задачах.
Примеры:
- Чат
- Многопользовательская онлайн-игра
- Рассылка оповещений и уведомлений
- Обновление страницы новыми данными без перезагрузки
- Быстрая коммуникация двух сервисов
Сервер Centrifugo
Сервер для работы с вебсокетами можно сделать и самому, но уже есть готовый сервер, очень производительный и простой в установке: Centrifugo.
Благодаря Centrifugo мы можем запустить сервер, из своего приложения просто отправлять сообщения на сервер по API, а он уже сам доставит сообщения всем подключенным клиентам через вебсокеты.
Установка Centrifugo
Используем Centrifugo V4.0.1, актуальную на сегодня.
Установку рассматриваю на примере Ubuntu 20.04, у меня Windows с WSL на которой установлен Ubuntu.
Открываем страничку с релизами https://github.com/centrifugal/centrifugo/releases, выбираем актуальный релиз для своей платформы.
В случае Ubuntu это centrifugo_4.0.1_linux_amd64.tar.gz
.
Копируем ссылку и скачиваем себе архив.
wget https://github.com/centrifugal/centrifugo/releases/download/v4.0.1/centrifugo_4.0.1_linux_amd64.tar.gz
Распаковываем архив.
tar -xvzf centrifugo_4.0.1_linux_amd64.tar.gz
Проверяем, что файл запускается:
./centrifugo version
Настройка Centrifugo
Создаём файл конфигурации.
./centrifugo genconfig
Будет создан файл config.json
с случайным образом сгенерированными ключами.
Добавляем следующие параметры:
{
...,
"admin": true,
"allowed_origins": [
"*"
],
"allow_subscribe_for_client": true,
"presence": true,
"history_size": 100,
"history_ttl": "30000000s"
}
Админка Centrifugo
В комплекте с сервером Centrifugo идёт встроенная админка.
Запускаем сервер и админка тоже запустится:
./centrifugo
По умолчанию сервер настроен на порт 8000, там же и откроется наша админка:
http://localhost:8000
Открываем админку. Для входа в админку используем логин и пароль из файла config.json
.
Эта панель может пригодиться нам в будущем для отладки.
Создаём клиент вебсокета на JavaScript
Создаём файл index.html
:
<html>
<head>
<title>Centrifugo quick start</title>
</head>
<body>
<div id="counter">-</div>
<script src="https://unpkg.com/centrifuge@3.0.0/dist/centrifuge.js"></script>
<script type="text/javascript">
const container = document.getElementById('counter');
const centrifuge = new Centrifuge("ws://localhost:8000/connection/websocket", {
token: "YOUR_TOKEN"
});
centrifuge.on('connecting', function (ctx) {
console.log(`connecting: ${ctx.code}, ${ctx.reason}`);
}).on('connected', function (ctx) {
console.log(`connected over ${ctx.transport}`);
}).on('disconnected', function (ctx) {
console.log(`disconnected: ${ctx.code}, ${ctx.reason}`);
}).connect();
const sub = centrifuge.newSubscription("channel");
sub.on('publication', function (ctx) {
container.innerHTML += '<br>' + ctx.data.value;
}).on('subscribing', function (ctx) {
console.log(`subscribing: ${ctx.code}, ${ctx.reason}`);
}).on('subscribed', function (ctx) {
console.log('subscribed', ctx);
}).on('unsubscribed', function (ctx) {
console.log(`unsubscribed: ${ctx.code}, ${ctx.reason}`);
}).subscribe();
</script>
</body>
</html>
Это веб-страница с клиентским подключением к серверу Centrifugo.
Как только с сервера придёт сообщение, оно сразу же появится на странице.
Нам понадобится веб-сервер чтобы запустить её как полноценный веб-сервис.
Далеко ходить не будем, Centrifugo и это умеет:
./centrifugo serve 3000
Теперь открываем страницу по адресу http://localhost:3000 .
Создаём токен JWT
Сейчас подключиться к серверу Centrifugo со страницы не получится, так для подключения необходим токен JWT.
Мы могли бы создать его сами, но к счастью Centrifugo может и это сделать за нас:
./centrifugo gentoken -u 123456
Здесь 123456
это ID вымышленного пользователя, для которого мы создаём токен.
Копируем токен из вывода команды, вставляем в файл index.html
вместо текста YOUR_TOKEN
.
Обновляем страницу. Теперь подключение должно выполниться успешно.
Отправка в вебсокет из CURL
У нас есть сервер вебсокетов, есть клиент на JS который “слушает” все сообщения и выводит их пользователю.
Для отправки сообщения на сервер нам нужно отправить HTTP-запрос на API Centrifugo.
Сделаем это через CURL:
curl --header "Content-Type: application/json" \
--header "Authorization: apikey YOUR_API_KEY" \
--request POST \
--data '{"method": "publish", "params": {"channel": "channel", "data": {"value": "Hello from CURL!"}}}' \
http://localhost:8000/api
В заголовке Authorization
вместо YOUR_API_KEY
нужно указать параметр api_key
из config.json
.
Выполнив эту команду, убеждаемся что сообщение доставлено на клиентской странице (http://localhost:3000).
Отправка в вебсокет из PHP
Создаём проект PHP.
mkdir centrifugo-php-client
cd centrifugo-php-client
composer init
Подключаем клиентский модуль для API Centrifugo.
composer require centrifugal/phpcent
Создаём файл send.php
<?php
require "./vendor/autoload.php";
if (count($argv) < 2) {
die('Нужно указать сообщение в качестве параметра');
}
$message = $argv[1];
$client = new \phpcent\Client("http://localhost:8000/api");
$client->setApiKey("YOUR_API_KEY");
$result = $client->publish("channel", ["value" => $message]);
var_dump($result);
Вместо YOUR_API_KEY
указываем ключ API из параметра api_key
в файле config.json
.
Отправляем сообщение через PHP:
php send.php "Hello from PHP!"
Открываем вкладку клиентского приложения и убеждаемся что сообщение пришло.
Готово. Теперь на основе этого кода мы сможем создать своё собственное приложение использующее вебсокеты.
Отправка в вебсокет из клиента
Так как вебсокет позволяет двустороннее общение клиента и сервера, мы можем отправлять сообщения напрямую с клиента.
Отправленные сообщения будут доставлены всем подписчикам того канала, в который мы отправили сообщение.
Чтобы эта возможность заработала, нужно включить опцию allow_publish_for_subscriber
в файле config.json
.
Бонус. Прослушка сообщений на PHP
Для отладки сетевых взаимодействий я предпочитаю открывать несколько окон консоли bash
: в одном окне отправляешь сообщение, в соседнем видишь что оно доставлено.
К сожалению, используя библиотеку centrifugal/phpcent
мы можем только отправлять сообщения в Centrifugo.
Мы не сможем с помощью этой библиотеки прослушивать сообщения сервера подключившись к вебсокету, как мы делали это в JS.
Но мы можем вытащить историю сообщений по API.
Файл history.php
:
<?php
require "./vendor/autoload.php";
$client = new \phpcent\Client("http://localhost:8000/api");
$client->setApiKey("YOUR_API_KEY");
$history = $client->history("channel", -1);
$messages = $history->result->publications;
foreach ($messages as $message) {
echo "{$message->data->value}\n";
}
Заменяем YOUR_API_KEY
на ключ API из параметра api_key
в файле config.json
.
Теперь запускаем этот файл раз в секунду и получаем прослушку сообщений, не через вебсокеты а через API.
watch -n 1 php history.php