Для чего нужны веб-сокеты
Веб-сокеты (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/











Нет Ответов