Как создавать, принимать и обрабатывать socket?
Здравствуйте, хочу сделать чат между авторизованными пользователями, уже установлены на сервер nodejs и websocket, все настроено и через netcat попробовал с двух терминалов вести переписку, но не совсем понимаю как реализовать это на сайте, написан на php и js, есть ли самый простой пример отправки сообщения на сервер и без перезагрузки страницы сразу его отображения у другого человека? Не совсем понятно как это реализовать, и не понятно что именно отправлять порту на котором висит прослушивание, допустим я туда отправляю обычную ссылку с get параметрами в которых содержится user_id от кого и user_id кому, а также ключ сообщения которое хранится в базе данных, дальше как эти данные обработать без перезагрузки, а главное принять и отобразить у того, кому оно предназначено, а не у всех подряд. Как вообще выглядит эта цепочка на примере?
Дополнительно:
1. Вебсокеты - это сложновато.
2. Вебсокеты на PHP - вдвойне сложно. Проблема в том, что PHP задуман как скриптовый язык, т.е. скрипт выполняется заканчивает свою работу. А вебсокет - это постоянное соединение, т.е нам надо, чтобы программа постоянно крутилась в фоне. Вебсокеты можно реализовать на PHP, но, как персонально мне кажется, проще будет выучить Go )) , или же, как в ответе уважаемого Артём , сделать сервер на ноде.
3. Если ваш чат не такой супер-функциональный, как чат в мессенджерах, то вместо вебсокетов можно обойтись SSE (Server Sent Events). SSE так же требует постоянного соединения, но всё работает через HTTP, и это ну прямо намного проще. Единственный недостаток - это то, что SSE работает только в одну сторону: от сервера в браузер. Т.е. запросы из браузера можно получать обычным POST запросом, а отдавать обратно информацию уже через SSE.
С SSE есть два пути:
1. Написать сервер самому, используя какую-то простую библиотеку вроде этой https://github.com/hhxsv5/php-sse
2. Но я бы сделал ещё проще. Есть такой великолепный проект под названием Mercure https://mercure.rocks
Это отдельный сервис на Go, задача которого как раз поддерживать SSE соединение и отправлять сообщения в браузеры. Сервис сидит в фоне, а браузеры подписываются на события через EventSource буквально в три строчки, как описано тут https://mercure.rocks/docs/getting-started
Прелесть этого в том, что для того, чтобы отправить сообщение всем браузерам из кода на PHP, вам надо просто сделать обычный POST запрос на специальный адрес этого сервиса Mercure с телом самого сообщения и его id. Т.е. вам не надо делать никаких долгоживущих процессов на PHP, всё будет работать как раньше.
Т.е. подытожим:
- Браузеры пользователей подписываются на события в Mercure
- Пользователь 1 отправляет текстовое сообщение обычным POST запросом на обычный PHP сайт.
- PHP сайт получает этот POST запрос, определяет, что его надо отправить Пользователю 2, и отправляет соответствующее сообщение обычным POST запросом в сервис Mercure
- Mercure отправляет сообщение Пользователю 2 через SSE, на которые он подписан.
- Сообщение появляется у него на страничке
- Как задумывался PHP и какой он сейчас - это абсолютно разные языки. Сейчас, нет никакой проблемы написать демона в PHP ("чтобы программа постоянно крутилась в фоне"), соответственно и web-sockets тоже.
Единственная сложность - это должно быть понимание асинхронности, но это безотносительно к языку.
Действительно, в какой-то степени, возможно будет проще написать web-sockets на Go, но это если у нас просто чат и не требуется обработка запросов, иначе реализация на тредах в Go могут оказаться гораздо сложнее, чем реализация через потоки в PHP.
И да, соглашусь, для такой задачи и если нет опыта, то лучше SSE. - Vitsliputsli,
Я согласен с вами, что PHP уже другой. Сам прыгаю от счастья последнее время, потому что PHP - это мой кусок хлеба. Но всё равно на нём всё ещё очень просто написать долгоживущий процесс, который будет течь по памяти. Поэтому до сих пор живёт и процветает тот же Swoole и прочие.
Подобные веб-серверы для той же ноды уже проверены годами, заматерели, обросли тоннами качественных туториалов.
И когда вместо того, чтобы решать бизнес-задачу, придётся возиться с утечкой памяти в PHP, будет крайне неприятно. - Виктор Кожухарь, а можете дать ссылку на баг с утечкой памяти в этих библиотеках PHP? Не видел подобного в последние годы. Ну а утечка созданная разработчиком скрипта лишь на его совести и безотносительна к языку.
- Vitsliputsli,
К библиотекам у меня больших претензий нет. Они делают, что могут. Проблема в том, что они требуют особого подхода, особого окружения, и не всегда их получится легко интегрировать в уже работающий проект.
Сам чистый PHP до сих пор полностью не подходит под долгоживущие процессы, потому что если вы не будете досконально знать, как работает сборщик мусора, не будете микромэнеджить жизнь каждого созданного объекта, он у вас обязательно потечёт. Поэтому, есть такая практика: перезапускать такие процессы через какое-то определённое время, либо когда размер памяти превысит какое-то пороговое значение, либо просто отталкиваться от количества каких-то логических операций, выполняемых этим процессом. А потом давать процессу SIG_KILL, он будет мягко завершаться и перезапускаться.
Но любой перезапуск потребует и перезапуска соединения с браузером, т.е. надо будет и фронтенд усложнять. И оно нам надо? Только потому, что мы всеми зубами фанатично держимся за PHP?
Я считаю, что нужно выбирать инструмент под задачу, и если есть языки, на которых это реализовать проще и быстрее, то так надо и делать - Виктор Кожухарь, повторюсь, утечка созданная разработчиком лишь на его совести и безотносительна к языку. Доскональное знание как работает мусорщик тут ничем не поможет, он не серебрянная пуля, он не угадает, что нужно очищать, если разработчик этого явно не сделал. И нарваться на утечку в том же Go нисколько не сложнее, чем в PHP. Если переменные создаются бесконтрольно и не уничтожаются, когда стали ненужны, тут ни Go, ни другой язык не спасет. Да и микроменеджмент объектов тоже ни к чему, если у вас понятные и логичные зависимости, а если все на все завязано, на каждому шагу циклические ссылки и сам черт ногу сломит, что от чего зависит, то обязательно чтото будет "зависать" в памяти, и тут ни мусорщик, ни Go не спасет.
А практика перезапускать процессы, это тоже что перезапускать сервер, вместо того чтобы разобраться и настроить его должным образом. И я никогда не поверю, что кто-то пишет демоны на PHP с утечками памяти, а на Go вдруг начинает писать без утечек, ну не в языке ведь дело.
Ответы:
На самом деле WS в PHP не так уж и сложны. Можно взять RoadRunner и центрифугу, которые будут менеджить подключения и отправлять запросы в PHP и броадкастить из PHP.
Вот пример на ютубчике: https://www.youtube.com/watch?v=AR9aCQWwXmk
Кроме того, можно запустить PHP в event-loop'е и накодить свою реализацию для вебсокетов. Минимальный набор небольшой: https://github.com/buggregator/trap/tree/master/sr... , но это очень непростой путь и лучше не брать на себя реализацию и поддержку того, что уже реализовано и поддерживается другими.
Так что можно взять за базу swoole (https://habr.com/ru/articles/427589/) или amphp (https://github.com/amphp/websocket-server).
Mercure, как костыль над SSE, я бы не советовал, уж лучше брать сразу полноценный Websocket. Но это вкусовщина.
Я бы посоветовал использовать фреймворк php Laravel. Там из коробки есть возможность использовать сокеты. Почитайте документацию и посмотрите видео на ютубе: laravel pusher, laravel socket.io и тд.
- SvizzZzy , а как вы смотрите на ratchet? Я на нем сделал чат, но что, если скажем 10 к соединений будет одновременно на сервере держаться, не ляжет ? Как нагрузка вообще у него? Больше, чем через node и socket.io?
- viktorross, к сожалению не знаком с ratchet.
Я обычно использую в своих проектах socket.io.
Поднимаю сервер node socket.io, к нему клиенты подключаются через js библиотеку laravel echo.
Вся тяжелая логика обрабатывается на сервере посредством php laravel.Кстати, как вариант, у laravel так же из коробки, есть возможность использовать разные броадкастеры, можно не поднимать свой node socket.io сервер, а например использовать платное решение: https://pusher.com/
Ну а по поводу нагрузки, я думаю тут всё зависит в первую очередь от реализации (в плане оптимизации процессов) и от мощности сервера. 10к соединений это мало, но опять же, смотря каким объемом данных они обмениваются (вдруг видео в base64 туда сюда гоняют :)) )
Я бы порекомендовал экспериментировать, пробовать и решать проблемы по мере их поступления.
- SvizzZzy, спасибо большое))
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Для создания, принятия и обработки сокетов в PHP, можно использовать функции из расширения sockets. Ниже приведен пример кода, демонстрирующий основные шаги по работе с сокетами:
1. Создание сокета:
// Создаем сокет $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // Проверяем успешность создания сокета if ($socket === false) { echo "Ошибка при создании сокета: " . socket_strerror(socket_last_error()); }
2. Привязка сокета к адресу и порту:
// Привязываем сокет к адресу и порту $bind = socket_bind($socket, '127.0.0.1', 8888); // Проверяем успешность привязки if ($bind === false) { echo "Ошибка при привязке сокета: " . socket_strerror(socket_last_error($socket)); }
3. Прослушивание подключений:
// Начинаем прослушивание подключений $listen = socket_listen($socket); // Проверяем успешность начала прослушивания if ($listen === false) { echo "Ошибка при начале прослушивания: " . socket_strerror(socket_last_error($socket)); }
4. Принятие входящего соединения:
// Принимаем входящее соединение $client = socket_accept($socket); // Проверяем успешность принятия соединения if ($client === false) { echo "Ошибка при принятии соединения: " . socket_strerror(socket_last_error($socket)); }
5. Отправка и получение данных:
// Отправляем данные клиенту socket_write($client, "Hello, client!"); // Получаем данные от клиента $data = socket_read($client, 1024); // Выводим полученные данные echo "Полученные данные: " . $data;
6. Закрытие сокета:
// Закрываем соединение с клиентом socket_close($client); // Закрываем сокет socket_close($socket);
Это основные шаги по работе с сокетами в PHP. Обратите внимание, что для корректной работы с сокетами необходимо учитывать возможные ошибки и обрабатывать их соответствующим образом.