На хабре уже есть статья по этой теме. Но фреймворк с тех пор сильно обновился и, к сожалению, по старой статье разобраться скорее всего будет проблематично. Кроме того, в изучении чего-то нового всегда самое сложное — это начало. Поэтому по свежей памяти постараюсь описать процесс старта хотя бы в общих чертах.
Установка
В принципе, установка достаточно полно описана на официальном сайте.
Я предполагаю, что у вас уже установлен PHP с версией не младше 5.3. Поэтому описываю только остальное (на примере FreeBSD):
- Установить из портов необходимые библиотеки для PHP:
Это можно сделать по отдельности:
- php5-pcntl
# cd /usr/ports/devel/php5-pcntl # make install clean
- php5-shmop
# cd /usr/ports/devel/php5-shmop # make install clean
- php5-sockets
# cd /usr/ports/net/php5-sockets # make install clean
либо с помощью порта php5-extensions:
# cd /usr/ports/lang/php5-extensions # make config
Отмечаем галочки напротив PCNTL, SHMOP и SOCKETS и устанавливаем:
# make install clean
При втором подходе в конце можно получить ошибку, что порт php5-extensions уже установлен. Тем не менее, сами библиотеки будут установлены нормально.
- php5-pcntl
- Устанавливаем pecl-libevent::
# cd /usr/ports/devel/pecl-libevent # make install clean
- Если еще не установлен git, ставим (опции можно не менять):
# cd /usr/ports/devel/git # make install clean
- Устанавливаем phpDaemon как сказано на сайте:
# cd /usr/local # git clone git://github.com/kakserpom/phpdaemon.git # chmod +x phpdaemon/bin/phpd # ln -s /usr/local/phpdaemon/bin/phpd /usr/bin/phpd
В принципе, сама установка на этом завершена. Теперь стоит попробовать запустить первое приложение. Для этого возьмем пример из комплекта — ExampleWebSocket. Для этого необходимо сделать несколько шагов:
- Создаем папку applications в папке phpDaemon. Т.е., полный путь должен быть
/usr/local/phpdaemon/applications
. Здесь будут храниться наши приложения. - Копируем туда файл ExampleWebSocket.php из папки app-examples.
- Прописываем конфигурацию нашего WebSocket-сервера в файле
/usr/local/phpdaemon/conf/phpd.conf
:user www; group www; max-workers 8; min-workers 1; start-workers 1; max-idle 0; WebSocketServer { enable 1; listen "tcp://0.0.0.0"; listen-port 8047; privileged; } ExampleWebSocket {}
Теперь пробуем запустить наш сервер:
# phpd start
Все должно пройти нормально и в логах (/var/log/phpdaemon.log) мы должны увидеть примерно следущее:
Pool:WebSocketServer up.
W#73942 ExampleWebSocket up.
Spawning 7 worker(s).
W#73948 ExampleWebSocket up.
W#73943 ExampleWebSocket up.
W#73944 ExampleWebSocket up.
W#73945 ExampleWebSocket up.
W#73946 ExampleWebSocket up.
W#73947 ExampleWebSocket up.
W#73949 ExampleWebSocket up.
Пишем первое приложение
Хотелось бы предложить в дополнение к идущему в комплекте еще один пример WebSocket-приложения. Думаю, лишний пример никогда не будет вреден разбирающемуся в новом человеку.
Сразу следует оговориться, что в данный момент времени официальная документация сильно устарела. Разработчик обещает исправить это в будущем. Тем не менее, всегда можно открыть интересующий класс фреймворка, найти нужный метод и разобраться как он работает. Чаще всего это не отнимает много времени: код интуитивно понятен.
Итак:
- Создаем в папке applications новый файл с именем MyWebSocket.php со следующим кодом:
<?php class MyWebSocket extends AppInstance { public $enableRPC=true; // Без этой строчки не будут работать широковещательные вызовы public $sessions=array(); // Здесь будем хранить указатели на сессии подключившихся клиентов // С этого метода начинается работа нашего приложения public function onReady() { $appInstance = $this; // Метод timerTask() будет вызываться каждые 5 секунд $this->timerTask($appInstance); // Наше приложение будет доступно по адресу ws://site.com:8047/myws WebSocketServer::getInstance()->addRoute('myws', function ($client) use ($appInstance) { $session=new MyWebSocketRoute($client, $appInstance); // Создаем сессию $session->id=uniqid(); // Назначаем ей уникальный ID $this->sessions[$session->id]=$session; //Сохраняем в массив return $session; }); } function timerTask($appInstance) { // Отправляем каждому клиенту свое сообщение foreach($this->sessions as $id=>$session) { $session->client->sendFrame('This is private message to client with ID '.$id, 'STRING'); } // После отправляем всем клиентам сообщение от каждого воркера (широковещательный вызов) $appInstance->broadcastCall('sendBcMessage', array(Daemon::$process->pid)); // Перезапускаем наш метод спустя 5 секунд Timer::add(function($event) use ($appInstance) { $this->timerTask($appInstance); $event->finish(); }, 5e6); // Время задается в микросекундах } // Функция для широковещательного вызова (при вызове срабатывает во всех воркерах) public function sendBcMessage($pid) { foreach($this->sessions as $id=>$session) { $session->client->sendFrame('This is broadcast message from worker #'.$pid, 'STRING'); } } } class MyWebSocketRoute extends WebSocketRoute { public $client; public $appInstance; public $id; // Здесь храним ID сессии public function __construct($client,$appInstance) { $this->client=$client; $this->appInstance=$appInstance; } // Этот метод срабатывает сообщении от клиента public function onFrame($data, $type) { // Отправляем ему ответ $this->client->sendFrame('Server receive from client #'.$this->id.' message "'.$data.'"', 'STRING'); } // Этот метод срабатывает при закрытии соединения клиентом public function onFinish() { // Удаляем сессию из массива unset($this->appInstance->sessions[$this->id]); } }
- Правим наш конфигурационный файл до следующего вида:
user www; group www; max-workers 8; min-workers 1; start-workers 1; max-idle 0; Pool:WebSocketServer { enable 1; listen "tcp://0.0.0.0"; listen-port 8047; privileged; } MyWebSocket {}
- Создаем на компьютере HTML-файл со следующим содержимым:
<script type="text/javascript"> function add(text) { document.forms[0].b.value=text+"n"+document.forms[0].b.value; } if("WebSocket" in window) { var timer; var ws=new WebSocket("ws://site.com:8047/myws"); ws.onopen=function() { add('Connection opened'); timer=window.setInterval(function() { var date = new Date(); var message='ping at '+date.getSeconds(); ws.send(message); add('Client sent message "'+message+'"'); }, 30000); }; ws.onmessage=function(evt) { add('Message from server: "'+evt.data+'"'); }; ws.onclose=function() { add('Connection closed'); window.clearTimeout(timer); }; } else { alert("Your browser doesn't support WebSocket"); } </script> <form> <textarea name="b" style="width:100%;height:100%"/></textarea> </form>
- Перезапускаем демона:
# phpd restart
- Открываем наш HTML-файл в браузере (я проверял только в Opera 12.13 и Google Chrome 24.0.1312.57).
После этого JS-клиент начинает взаимодействовать с сервером и в браузере будут выводиться все их взаимодействия.
Примечание
У сервера имеется таймаут равный двум минутам (120 секунд). Соответственно, клиент должен периодически посылать серверу сообщения «пустышки», чтобы сервер не счел его неактивным и не отключил. И не забудьте заменить site.com на адрес вашего хоста.
Автор: bartwell