Для чего нужны веб-сокеты
Веб-сокеты (1) — это технология, позволяющая устанавливать непрерывное соединение между клиентом и сервером. Особенность такой системы также в том, что сервер может по своей инициативе отправлять данные одному или нескольким клиентам. Это позволяет создавать real-time мессенджеры, онлайн-игры и прочие проекты.
PHP, казалось бы, не подходит для такой цели. Он обычно используется для создания динамических веб-страниц и работает по принципу «открыл-ответил-закрыл». Что же, стереотипы пора ломать…
Пример работы с веб-сокетами
см. в (1) пример библиотеки по вебсокетам:
Данный класс избавляет вас от необходимости скачивания сторонних демонов или написания сервера самостоятельно. Всё, что вам потребуется — подключить класс и написать функции приёма сообщений, при желании ещё и heartbeat. Чтобы использовать класс, просто включите его в ваш скрипт
В (2) см. пример работы с сокетами — реализация чата.
Будем изучать простейший пример по мануалу (3), скачав исходники (4):
Проверка поддержки сокетов на хостинге
– Что нужно сделать в первую очередь для начала работы с WebSocket?
– Проверить поддержку сокетов на хостинге и в Денвере.
Для этого создаём простенький файлик sockettest.php
<?php /** * Проверка поддержки сокетов на хостинге */ if(extension_loaded('sockets')) { echo "WebSockets OK"; } else { echo "WebSockets UNAVAILABLE"; } ?>
Ответ:
php sockettest.php WebSockets UNAVAILABLE
- ошибка
Та же проверка в браузере: http://exp/websockets/petukhovsky/sockettest.php
WebSockets OK
- ОК (т.е. в консоли сокет не работает, а браузере по http норм)
Исходники
socket.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>Simple Web-Socket Client</title> </head> <body> <br/><br/> http://socket.js Server address: <input id="sock-addr" type="text" value="ws://echo.websocket.org"> <br/><br/> Message: <input id="sock-msg" type="text"> <input id="sock-send-butt" type="button" value="send"> <br/> <br/> <input id="sock-recon-butt" type="button" value="reconnect"> <input id="sock-disc-butt" type="button" value="disconnect"> <br/> <br/> Messages from web-socket: <div id="sock-info" style="border: 1px solid"></div> </body> </html>
socket.js
"use strict"; (function () { // private vars var socket; //////////////////////////////////////////////////////////////////////////// var init = function () { socket = new WebSocket(document.getElementById("sock-addr").value); socket.onopen = connectionOpen; socket.onmessage = messageReceived; socket.onerror = errorOccurred; /** * Send button click */ document.getElementById("sock-send-butt").onclick = function () { socket.send(document.getElementById("sock-msg").value); }; /* * Disconnect button click */ document.getElementById("sock-disc-butt").onclick = function () { connectionClose(); }; /** * Reconnect button click */ document.getElementById("sock-recon-butt").onclick = function () { socket = new WebSocket(document.getElementById("sock-addr").value); socket.onopen = connectionOpen; socket.onmessage = messageReceived; }; }; // open socket connection function connectionOpen() { socket.send("Connection with "" + document.getElementById("sock-addr").value + "" Connection SUCCESS."); } // get message function messageReceived(e) { console.log("Server response: " + e.data); document.getElementById("sock-info").innerHTML += ("Message: " + e.data + "<br />"); } // error message function errorOccurred(e) { console.log("Server error: " + e.data); document.getElementById("sock-info").innerHTML += ("Error: " + e.data + "<br />"); } // close socket connection function connectionClose() { socket.close(); document.getElementById("sock-info").innerHTML += "Connection closed<br />"; } return { // ---- onload event ---- load: function () { window.addEventListener('load', function () { init(); }, false); } } })().load();
simpleworking.php
<?php /** * WebSocket server * @source https://petukhovsky.com/simple-web-socket-on-php-from-very-start/ * @hint поправолено под современную версию php 7.3+ * @return false|resource */ /** * Run websocket server * @return false|resource */ function go() { $starttime = round(microtime(true), 2); echo "GO() ... <br />rn"; echo "socket_create ..."; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (!$socket) { echo "Error: " . socket_strerror(socket_last_error()) . "<br />rn"; exit(); } else { echo "OK <br />rn"; } echo "socket_bind ..."; // привязываем его к указанным ip и порту // !! если возникает ошибка коннекта, меняет тут порт на любой другой $bind = @socket_bind($socket, '127.0.0.1', 8890); if (!$bind) { echo "Error: " . socket_strerror(socket_last_error()) . "<br />rn"; exit(); } else { echo "OK <br />rn"; } // разрешаем использовать один порт для нескольких соединений socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); echo "Listening socket... "; $listen = socket_listen($socket, 5); // слушаем сокет if (!$listen) { echo "Error: " . socket_strerror(socket_last_error()) . "<br />rn"; exit(); } else { echo "OK <br />rn"; } // Бесконечный цикл ожидания подключений while (true) { echo "Waiting... "; // Зависаем пока не получим ответа $accept = @socket_accept($socket); if ($accept === false) { echo "Error: " . socket_strerror(socket_last_error()) . "<br />rn"; usleep(100); } else { echo "OK <br />rn"; echo "Client "" . $accept . "" has connected<br />rn"; } $msg = "Hello, Client!"; echo "Send to client "" . $msg . ""... "; socket_write($accept, $msg); echo "OK <br />rn"; if ((round(microtime(true), 2) - $starttime) > 100) { echo "time = " . (round(microtime(true), 2) - $starttime); echo "EXIT.<br />rn"; return $socket; } sleep(10); // засыпаем на 10 сек } } error_reporting(E_ALL); // Выводим все ошибки и предупреждения set_time_limit(0); // Время выполнения скрипта не ограничено ob_implicit_flush(); // Включаем вывод без буферизации $socket = go(); // Функция с бесконечным циклом, возвращает $socket по запросу выполненному по прошествии 100 секнуд. echo "go() ended<br />rn"; if (isset($socket)) { echo "Closing connection... "; @socket_shutdown($socket); socket_close($socket); echo "OK <br />rn"; }
Тестируем
Открываем веб форму:
http://exp/websockets/petukhovsky/socket.html
Шлем сообщения и получаем их тут же:
Слушаем сервер: http://exp/websockets/petukhovsky/simpleworking.php
Во вкладке клиента в поле Server address вводим ws://127.0.0.1:889 и нажимаем reconnect, мы видим что на клиенте ничего не происходит:
а на сервере появляются сообщения вида:
Чтобы окончательно убедиться в том, что сервер отвечает, что его сообщения не блокируются файрволом, запустите скрипт сервера http://exp/websockets/simpleworking.php запустите telnet и попытайтесь подключиться к 127.0.0.1:889, только это нужно сделать не позднее 100 секунд, с момента запуска сервера, пока он не закрыл соединения и не завершил скрипт.
По telnet должен придти ответ “Hello, Client!”, что свидетельствует о том, что всё работает в штатном режиме и связь с сервером двухсторонняя.
Фиксим ошибки
У меня выдается ошибка при коннекте:
ошибка запуска теперь: http://exp/websockets/petukhovsky/simpleworking.php
также теперь ошибка коннкета в клиенте:
Все сообщения отправляемые по протоколу WebSocket можно разделить на несколько видов: handsnake (рукопожатие при установлении связи), ping-pong(проверка связи) и data-transfer(передача данных). Также есть более краткое описание протокола в общих чертах на русском в (5)
Fix: меняем порт в simpleworking.php:
Теперь вроде работает, сервер получает сообщения:
C этим разобрались , можно еще изучить расширенный урок по демонам в (6)
Источники:
(1) https://habr.com/ru/post/209864/
(2) https://itnan.ru/post.php?c=1&p=266931
(3) https://petukhovsky.com/simple-web-socket-on-php-from-very-start/
(4) Исходники клиента и сервера: https://petukhovsky.com/f/simple-web-socket-on-php-from-very-start/simpleworking.zip
(5) https://learn.javascript.ru/websockets
(6) https://petukhovsky.com/simple-web-socket-on-php-daemon/
Нет Ответов