Сокеты javascript и php сервер. Веб-сокеты. Стандартный пример вывода текущего времени сервера с обновлением до секунды

В данной статье рассмотрим работу с библиотекой phpws , которая нужна для организации приложений или WEB — приложений на основе сокетов и запустим парочку стандартных примеров, которые представлены на странице репозитория GitHub данного проекта.

Примечание. Сокеты у нас будут работать, как на серверной части, так и на клиентской. На серверной части этим займется стандартный WebSocket, который появился в HTML5, а работу на серверной части, где у нас PHP будет выполнять библиотека phpws. Есть много подобных библиотек, пожалуй, особенно следует отметить Ratchet , который мне показался громоздким для моего маленького проекта и я остановился на phpws.

Нам нужен Composer

Очень удобная штука, которая облегчит всю работу с зависимостями и библиотеками, которые включены в проекты. По всеобщему стандарту кодирования или, проще говоря, по правильному написанию кода все библиотеки, пакеты, зависимости или проекты принято хранить в репозиториях исходных кодов, которые потом подключаются в проект за парочку команд через менеджеры пакетов или через менеджеры зависимостей. Для каждого языка есть свой менеджер или почти для каждого, поэтому, вооружимся данным инструментом и установим его в систему командой в Linux

$ curl -s https://getcomposer.org/installer | php

Мы его скачали, но команды composer не будут выполняться через PATH, поэтому переместим скачанное в /usr/local/bin

$ mv composer.phar /usr/local/bin/composer

Выполняем команду и получаем результат в виде инструкций и команд Composer, что говорит об удачной установке

$ composer

Для Windows и Mac можно посмотреть инструкцию на офф-сайте .

Примечание. Все зависимости, которые нужно подключать надо указывать в файле composer.json в корне проекта, который скачает, обновит и соберет все зависимости в одну папку vendor, из которого потом можно загружать через автозагрузчик классов. У Composer есть свое хранилище пакетов и библиотек и называется Packagist , который позволяет указывать vendor/package и он будет установлен. Да, можно указывать конкретные адреса svn/git репозиториев в composer.json, но это неудобно. Намного удобнее иметь какой-то центральный пункт, где есть соответствия пакетов с их адресами репозиториев. Это Packagist .

Нам нужна библиотека phpws

Для подключения к проекту, нам нужно зайти в корень папки проекта или в подпапку, если это будет частью проекта и там установить данную библиотеку, но сначала надо создать в этом месте composer.json, который выполним потом через консоль командами composer и он прочитав, все нам установит. Для этого создаем данный файл со следующим содержимым

{ "repositories": [ { "type": "vcs", "url": "https://github.com/Devristo/phpws" } ], "require": { "devristo/phpws": "dev-master" } }

В данном случае, мы указали, что скачивать прямо с репозитория GitHub без посредничества Packagist.

Выполним данный файл командой

$ composer install

После чего в папке появится подпапка vendor со скачанными библиотеками и нам остается их подключить и использовать.

Нам нужны базовые понимания работы WebSocket с PHP

И так, на минуточку разберемся, что делать со скачанными библиотеками и как их использовать, углубляться не буду, поэтому, если на пальцах, то нам нужно 2 файла:

  • client.html — файл клиентской части, который видит тот, кто за браузером. В нем с сокетом работает JavaScript;
  • server.php — собственно, наш сокет-сервер, который обрабатывает все запросы от клиента и отвечает ему обработанными обтветами.
  • Для соединения нам надо указать схему соединения или протокол связи, ip — адрес сервера. Если удаленный сервер, то надо указать ip — адрес хоста или VPS, а если локальный, то localhost, который равен адресу 127.0.0.0 и указываем еще порт, на котором служба сервера будет запущена под собственным PID. Все эти данные указываются при создании экземпляра соединения.

    Для клиентской части:

    Var socket = "ws://127.0.0.0:12345/";

    Для серверной части:

    $server = new WebSocketServer("tcp://127.0.0.0:12345", $loop, $logger);

    Стандартный пример вывода текущего времени сервера с обновлением до секунды

    Для работы данного примера нужно единожды запустить файл server.php через консоль и после выполнения данного скрипта запуститься сокет-сервер со своим PID

    Что делает пример? В примере показано, как до долей секунды сокет обновляет информацию времени на сервер и выдает его клиенту

    Клиентская часть:

    Timers Server Time Status: Time:

    Var socket = new WebSocket("ws://localhost:12345"); socket.onopen = function(msg){ document.getElementById("status").innerHTML = "Online"; }; socket.onclose = function(msg){ document.getElementById("status").innerHTML = "Offline"; } socket.onmessage = function(msg){ document.getElementById("time").innerHTML = msg.data; };

    Серверная часть:

    #!/php -q

    Код хорошо прокомментирован, поэтому, думаю, что здесь всё предельно понятно. Алгоритм работы клиента тривиальный: создание сокета, подключение к серверу, отправка запросов, получение ответов, закрытие соединения. Мы с Вами отправили число 15 . Если Вы читали предыдущую статью, то помните, что задача сервера это число возвести в квадрат и вернуть его. Поэтому если Вы запустите этот клиент, то увидите от сервера 225 (15*15 ). Потом мы подаём команду shutdown , которая останавливает сервер.

    Теперь у Вас есть минимальный набор знаний по работе с сокетами, а вообще тема очень интересная, поэтому Вы можете изучить её более детально. Вы можете создавать очень сложные клиент-серверные приложения, к котором Вы всегда сможете подключиться и отправлять самые различные запросы, которые сервер будет обрабатывать.

    (Web Sockets) - это передовая технология, которая позволяет создавать интерактивное соединение между клиентом (браузером) и сервером для обмена сообщениями в режиме реального времени. Веб-сокеты, в отличие от HTTP, позволяют работать с двунаправленным потоком данных, что делает эту технологию совершенно уникальной. Давайте разберемся, как работает эта технология и чем она отличается от HTTP.

    Как работает HTTP?

    Схема обмена сообщениями по HTTP

    Вы наверняка знаете, что такое HTTP (или HTTPS), поскольку встречаетесь с этим протоколом каждый день в своём браузере. Браузер постоянно спрашивает у сервера, есть ли для него новые сообщения, и получает их.

    Вы также можете знать, что HTTP позволяет использовать разные типы запросов, такие как POST, GET или PUT, каждый из которых имеет своё назначение.

    Как работают веб-сокеты?

    Схема обмена сообщениями при использовании веб-сокетов

    Веб-сокетам же для ответа не нужны ваши повторяющиеся запросы. Достаточно выполнить один запрос и ждать отклика. Вы можете просто слушать сервер, который будет отправлять вам сообщения по мере готовности.

    Веб-сокеты можно использовать, если вы разрабатываете:

    • приложения реального времени;
    • чат-приложения;
    • IoT-приложения;
    • многопользовательские игры.
    Когда следует избегать использования веб-сокетов?

    Практически никогда. Единственный минус - это несовместимость с некоторыми браузерами, но уже 95 % браузеров поддерживают веб-сокеты.

    В некоторых случаях веб-сокеты вам всё же не понадобятся. Если вы создаёте простую CMS, вам вряд ли пригодится функциональность в режиме реального времени. Также не стоит использовать веб-сокеты в REST API, поскольку вам хватит таких HTTP-запросов, как GET, POST, DELETE и PUT.

    Практические примеры

    В примерах ниже для клиента используется JavaScript, а для сервера - Node.js. Примеры очень просты и вряд ли пригодятся на практике, но зато позволят разобраться в сути.

    Веб-сокеты

    Пример чата с веб-сокетом let ws = new WebSocket("ws://localhost:8080"); // выводим новые сообщения в консоль ws.onmessage = ({data}) => { console.log(data); } // отправляем сообщение ws.onopen = () => ws.send("Text");

    Const WebSocket = require("ws"); // создаём новый websocket-сервер const wss = new WebSocket.Server({ port: 8080 }); // отправляем клиентам, когда функция clientValidator возвращает true. this - это wss. wss.broadcast = function(data, clientValidator = () => true) { this.clients.forEach(client => { if (clientValidator(client)) { client.send(data); } }); } wss.on("connection", ws => { // событие будет вызвано, когда клиент отправит сообщение ws.on("message", message => { // отправляем сообщение всем, кроме автора wss.broadcast(message, client => client !== ws); }); });

    Вот иллюстрация работы веб-сокетов:

    Демонстрация работы веб-сокетов

    Эквивалент в HTTP

    Так как HTTP должен постоянно проверять канал на наличие новых сообщений, можно использовать «грязную» проверку (dirty check) - подход, при котором клиент с заданной периодичностью (допустим, каждые 200 мс) проверяет наличие новых сообщений на сервере.

    Как Яндекс использует ваши данные и машинное обучение для персонализации сервисов - .

    Сатья посвещена сетевому програамированию, а в частности — программированию сокетов на PHP. Для сетевого взаимодействия в PHP существует две категории функций:

  • Функция fsockopen(string hostname, integer port, integer error_number, string error_description, double timeout) — она открывает сетевое соединение как файловый поток и возвращает дискриптор файла с которым работают функции fputs, fgets и т.д.
  • Функции которые передают информацию непосредственно на уровне IP-протокола. И это гораздо более низкий уровень по сравнению с уровнем на котором работает функция fsockopen.
  • Рассматриваться будут только функции под номером 2, т.к. они более интересны.
    Для начала проверим, подключена ли у Вас библиотека работы с сокетами.

    Проверить это можно следующим скриптом:

    If(extension_loaded("sockets")) echo "расширение загружено"; else echo "расширение не загружено"; ?>

    Если расширение не загружено, то Вам следует его загрузить.

    И так. Наиболее простой в рамках статьи пример — echo-сервер. Эхо-сервер — это означает, что строка отправленная клиентом серверу, возвращается обратно. То есть сервер получает какое-то сообщение от клиента, что-то с ним делает и отправляет ему обратно.

    У нас будет 2 скрипта:

  • Сервер или демон (daemon).
  • Клиент.
  • Скрипт «Клиент».

    Для реализации клиента нам понадобятся следующие функции работающие с сокетами:

  • socket_create (integer family, integer socket_type, integer protocol); — функция создаёт сокет и возвращает ресурс сокета. Первым аргументом является семейство протокола, Если соединение будет через Internet, то задаваемое значение должно быть — AF_INET; Если соединение будет происходить через сокеты UNIX — AF_UNIX; Вторым аргументом является тип сокета. Обычно используются SOCK_STREAM для TCP взаимодействия и SOCK_DGRAM для UPD взаимодействия. Третий аргумент задаёт протокол SOL_TCP или SOL_UPD в зависимости от типа.
  • socket_connect (resource socket, string address, integer port); — после создания сокета необходимо к нему подключится. Первым аргументом является ресурс созданного сокета, вторым IP адрес сокета если семейство протокола AF_INET, или pathname сокета Unix-домена если сокет из семейства AF_UNIX. Третьем агрументом является номер порта с которым должно быть установлено соединение.
  • socket_read (resource socket, integer length, integer type); — функция считывает заданное в аргументе lenght количество байт из указанного сокета. По умолчанию чтение производится без учета управляющих символов, или можно задать в аргументе type — PHP_BINARY_READ, для учета управляющих символов необходимо задать значение PHP_NORMAL_READ.
  • socket_write (resource socket, string buffer, integer length); — функция записывает данные в сокет.
  • socket_close (resource socket); — закрывает сокет и освобождает память.
  • Листинг 1.0 — Клиент

    Set_time_limit(0); ob_implicit_flush(); echo "- Клиент

    "; $address = "127.0.0.1"; // адресс localhost. $port = 5555; // порт с которым будет установленно соединение. echo "Создание сокета... "; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($socket "; } else { echo "OK
    "; } echo "Подключение к сокету... "; $connect = socket_connect($socket, $address, $port); if($connect === false) { echo "Ошибка: ".socket_strerror(socket_last_error())."
    "; } else { echo "OK

    "; $msg = "Hello Сервер!"; echo "Говорим серверу \"".$msg."\"..."; socket_write($socket, $msg, strlen($msg)); echo "OK
    "; echo "Сервер сказал: "; $awr = socket_read($socket, 1024); echo $awr."
    "; $msg = "exit"; echo "Говорим серверу \"".$msg."\"..."; socket_write($socket, $msg, strlen($msg)); echo "OK
    "; } if(isset($socket)) { echo "Закрываем соединение... "; socket_close($socket); echo "OK
    "; } ?>

    Скрипт «Сервер».

    Для реализации сервера нам понадобятся следующие функции работающие с сокетами:

  • Все те функции которые были описаны выше.
  • socket_bind (resource socket, string address, integer port); — функция привязывает адрес к сокету. Аргумент addres — IP адрес сокета если семейство протокола AF_INET, или pathname сокета Unix-домена если сокет из семейства AF_UNIX.
  • socket_listen (resource socket, integer backlog) — функция прослушивает входящие соединения в сокет. Необязательный второй аргумент устанавливает максимальный размер очереди запросов, ожидающих соединения.
  • socket_accept (resource socket); — После того как сокет создан, привязан, и начал прослушивание, именно эта функция делает сервер сервером. Функция принимает входящие соединения.
  • Листинг 1.1 — Сервер

    Set_time_limit(0); ob_implicit_flush(); echo "- Сервер

    "; $address = "127.0.0.1"; $port = 5555; echo "Создание сокета... "; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if($socket "; } else { echo "OK
    "; } echo "Привязывание сокета... "; $bind = socket_bind($socket, $address, $port); if($bind "; } else { echo "OK
    "; } echo "Прослушивание сокета... "; $listen = socket_listen($socket, 5); if($listen "; } else { echo "OK
    "; } while(true) { echo "Ожидаем... "; $accept = socket_accept($socket); if($accept "; break; } else { echo "OK
    "; } $msg = "Hello, Клиент!"; echo "Отправить клиенту \"".$msg."\"... "; socket_write($accept, $msg, strlen($msg)); echo "OK
    "; while(true) { $awr = socket_read($accept, 1024); if (false === $awr) { echo "Ошибка: ".socket_strerror(socket_last_error())."
    "; break 2; } else { if (trim($awr) == "") break; else echo "Клиент сказал: ".$awr."
    "; } if ($awr == "exit") { socket_close($accept); break 2; } echo "Сказать клиенту \"".$msg."\"... "; socket_write($accept, $awr, strlen($awr)); echo "OK
    "; } } if (isset($socket)) { echo "Закрываем соединение... "; socket_close($socket); echo "OK
    "; } ?>

    Вот собственно и всё. Вначале запустите скрипт сервер, он создаст, привяжет, начнёт прослушивание сокета и установится в режим ожидания клиента. Далее запустите клиента.

    Так же хочу отметить что наиболее полезной является функция:
    socket_select (array read, array write, array except, integer timeout_seconds, integer timeout_microseconds); — Функция контролирует изменения происходящие на узлах. PHP просматривает поступления новых дынных на сокетах, заданных в массиве read. PHP просматривает готовность к приёму данных на сокетах, заданных в массиве write. PHP просматривает на наличие ошибок потоки, заданные в аргументе except. В таймаутах задается время, по истечению которого функция будет возвращать кол-во сокетов изменивших своё состояние или FALSE.

    Эта функция не заменима для мониторинга клиентов висящих на сокете.

    В статье приведена малая часть всех функций работающих сокетами, кого заинтересовало, откапывайте мануалы, читайте… Удачных экспериментов…