Быстрый старт с WebSocket на основе phpDaemon

от автора

На хабре уже есть статья по этой теме. Но фреймворк с тех пор сильно обновился и, к сожалению, по старой статье разобраться скорее всего будет проблематично. Кроме того, в изучении чего-то нового всегда самое сложное — это начало. Поэтому по свежей памяти постараюсь описать процесс старта хотя бы в общих чертах.

Установка

В принципе, установка достаточно полно описана на официальном сайте.
Я предполагаю, что у вас уже установлен PHP с версией не младше 5.3. Поэтому описываю только остальное (на примере FreeBSD):

  1. Установить из портов необходимые библиотеки для 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 уже установлен. Тем не менее, сами библиотеки будут установлены нормально.

  2. Устанавливаем pecl-libevent::
    # cd /usr/ports/devel/pecl-libevent # make install clean 
  3. Если еще не установлен git, ставим (опции можно не менять):
    # cd /usr/ports/devel/git # make install clean 
  4. Устанавливаем 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. Для этого необходимо сделать несколько шагов:

  1. Создаем папку applications в папке phpDaemon. Т.е., полный путь должен быть /usr/local/phpdaemon/applications. Здесь будут храниться наши приложения.
  2. Копируем туда файл ExampleWebSocket.php из папки app-examples.
  3. Прописываем конфигурацию нашего 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-приложения. Думаю, лишний пример никогда не будет вреден разбирающемуся в новом человеку.
Сразу следует оговориться, что в данный момент времени официальная документация сильно устарела. Разработчик обещает исправить это в будущем. Тем не менее, всегда можно открыть интересующий класс фреймворка, найти нужный метод и разобраться как он работает. Чаще всего это не отнимает много времени: код интуитивно понятен.

Итак:

  1. Создаем в папке 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]); 	}  } 

  2. Правим наш конфигурационный файл до следующего вида:
    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 {} 

  3. Создаем на компьютере 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://didrov.ru: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> 

  4. Перезапускаем демона:
    # phpd restart 

  5. Открываем наш HTML-файл в браузере (я проверял только в Opera 12.13 и Google Chrome 24.0.1312.57).

После этого JS-клиент начинает взаимодействовать с сервером и в браузере будут выводиться все их взаимодействия.

Примечание

У сервера имеется таймаут равный двум минутам (120 секунд). Соответственно, клиент должен периодически посылать серверу сообщения «пустышки», чтобы сервер не счел его неактивным и не отключил.

ссылка на оригинал статьи http://habrahabr.ru/post/168059/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *