сокеты php что это
Делаем вебсокеты на PHP с нуля
Некоторое время назад я выбирал библиотеку для работы с вебсокетами. На просторах интернета я натыкался на статьи по интеграции node.js с yii, а почти все статьи о вебсокетах на хабре ограничивались инструкциями к тому, как использовать phpdaemon.
Я изучал библиотеки phpdaemon и ratchet, они достаточно монструозны (причём используя ratchet для отправки сообщения конкретному пользователю рекомендовано дополнительно использовать wamp). Мне не совсем было понятно для чего использовать таких монстров, которые требуют установку других монстров. Почитав исходники этих, а также других библиотек, я разобрался как всё устроено и мне захотелось написать простой вебсокет-сервер на php самостоятельно. Это помогло мне закрепить изученный материал и наткнуться на некоторые подводные камни, о которых я не имел представления.
Так я решил написать необходимый для меня функционал с нуля.
Получившийся код и ссылка на демонстрационный чат в конце статьи.
Поставленные цели:
1) разобраться с серверными сокетами в php
2) разобраться с протоколом вебсокетов
3) написать с нуля простой сервер вебсокетов
1) Серверные сокеты в php
До этого момента я имел смутные представления о серверных сокетах. Почитав исходники нескольких библиотек для работы с вебсокетами я столкнулся с двумя схемами их реализаций:
используя расширение php «socket»:
или используя расширение php «stream»:
Я предпочёл второй вариант ввиду его краткости.
Итак, мы создали серверный сокет и теперь хотим обрабатывать новые соединения к нему, для этого опять же есть два варианта
или с использованием stream_select
Т.к. нам в дальнейшем нужно будет одновременно обрабатывать и серверный сокет на предмет новых соединений, и уже существующие подключения, на предмет новых сообщений, то остановимся на втором варианте.
2) Протокол вебсокетов
«Рукопожатие» или handshake:
Считываем значение Sec-WebSocket-Key из пришедшего заголовка от клиента, рассчитываем на его основе Sec-WebSocket-Accept и отправляем итоговый ответ:
обмен сообщениями
Простой сервер вебсокетов
Итак, у нас есть вся необходимая информация.
Используя код простого http сервера из первой части, а также функции handshake, decode и encode из второй мы можем собрать простой сервер вебсокетов.
В приведённом примере можно менять пользовательские сценарии onOpen, onClose и onMessage для реализации необходимого функционала.
Поставленные цели достигнуты.
Если этот материал вам покажется интересным, то в следующей статье я опишу как можно запускать несколько процессов для обработки соединений (один мастер и несколько воркеров), межпроцессное взаимодействие, интеграцию с вашим фреймворком на примере компонента yii.
Что такое сокет в PHP
Не так давно меня попросили рассказать про сокеты на PHP. Вообще я планирую написать ещё несколько статей в ближайшее время по этой теме, а в этой статье я расскажу о том, что такое сокеты в PHP. Чтобы Вы уже могли понять, нужны они Вам или нет.
Кратко резюмирую, что же такое сокет: есть клиент, есть сервер, есть правила взаимодействия (интерфейс), клиент, согласно этим правилам, посылает запрос, а сервер данный запрос принимает и, согласно тем же правилам, даёт ответ.
Как видите, всё совсем не сложно. Очень здорово то, что клиент и сервер могут быть написаны на совсем разных языках программирования и могут находиться друг от друга за тысячи километров. Мы же с Вами будет писать и клиент, и сервер в будущих статьях на языке PHP.
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Комментарии ( 2 ):
А способ взаимодействия клиента и сервера может называться протоколом? Например ftp.
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.
Copyright © 2010-2021 Русаков Михаил Юрьевич. Все права защищены.
PHP: Программирование сокетов
Сокеты представляют собой чрезвычайно удобную, но в то же время плохо понятую технологию взаимодействия между двумя процессами в сети. Эти процессы могут существовать на одной и той же машине, общаясь друг с другом через локальный сокет, предназначенный для взаимодействия между процессами, либо на разных машинах через Internet. Хотя тема сокетов очень обширна, в данной статье представлены основы, которые необходимы для использования расширений РНР, предназначенных для написания собственных серверов и клиентов сокетов.
�?мейте в виду, что примеры, приведённые далее в этой статье, разработаны для запуска непосредственно из окружения оболочки с использованием версии РНР командной строки. Хотя их можно запустить в Web-браузере, делать это не рекомендуется. В случае сценариев, которые создают серверы сокетов, их применение можно продемонстрировать с помощью любых программ, способных устанавливать сетевое соединение через сокеты, например, telnet (что, собственно, и рекомендуется).
Основы сокетов
Сокеты могут быть надежными, выполняющими все необходимое для обеспечения передачи данных из точки А в точку В (TCP), либо ненадежными, когда данные передаются без гарантии доставки (UDP).
Сокеты также бывают «блокирующими» и «неблокирующими». Блокирующие сокеты заставляют ваше приложение ожидать до тех пор, пока данные станут доступны, в то время как неблокирующие сокеты этого не делают. Хотя, как будет показано далее, все сокеты двунаправлены, все же существует разница между сокетами клиента и сервера.
Мы с вами рассмотрим ТСР-сокеты Internet, поскольку они наиболее широко используются на сегодняшний день. Тем не менее, концепции и примеры кода, приведенные здесь, применимы к большинству операций с сокетами.
Создание нового сокета
Независимо от типа создаваемого сокета (клиентский или серверный), все они инициализируются одинаковым способом — с помощью функции socket_create(). Синтаксис этой функции выглядит следующим образом:
В результате выполнения эта функция либо возвращает ресурс, представляющий созданный сокет, либо булевское значение false в случае ошибки.
Константы доменов для сокеткых соединений
Константа | Описание |
---|---|
AF_INET | Протокол Internet IPv4 |
AF_INET6 | Протокол Internet IPv6 |
AF_UNIX | Локальное межпроцессное взаимодействие |
После того, как домен установлен, нужно определить Т�?П подключения для создаваемого сокета. Эти типы перечислены в следующей таблице:
Константы типов сокетов
Константа | Описание |
---|---|
SOCK_STREAM | Последовательный надежный двунаправленный поток, основанный на подключении. �?спользуется наиболее часто. |
SOCK_DGRAM | Ненадежный сокет, без подключения, передающий данные фиксиро ванной длины. Очень хорош для потоков данных, в которых надеж ность не критична. |
SOCK_SEQPACKET | Подобен потоковым сокетам за исключением того, что данные пере даются н принимаются в виде пакетов фиксированной длины. |
SOCK_RAW | Неформатированное сокетное подключение, удобное для выполне ния операций ICMP (Internet Control Message Protocol — протокол управляющих сообщений Internet), таких как trace, ping и так далее. |
SOCK_RDM | Надежный, по непоследовательный сокет, подобный SOCK DGRAM. |
Как видите, существует множество опций при выборе типа создаваемых сокетов. Вообще большинство сокетных соединений устанавливается с помощью сокетов SОСК_STREAM или SOCK_DGRAM. Учитывая очевидную полноту SOCK_STREAM (большая часть Internet работает по этому типу сокетов через TCP), может быть не совсем понятно, зачем нужны сокеты типа SOCK_DGRAM (используемые с протоколом UDP).
В конце концов, почему вообще вам может понадобиться «ненадежный» способ передачи данных? Ответ становится очевидным, когда возникает необходимость в получении постоянного потока данных, который обрабатывается в реальном времени, от сервера к клиенту. Поскольку для приложений подобного типа потерянные пакеты несущественны (поскольку такие приложения имеют дело с данными, привязанными ко времени, которые быстро устаревают), нет необходимости в повторной отправке пакетов.
Мы будем испольэовать сокеты Internet IPv4 типа SOCK_STREAM, работающие через соединения SOL_TCP (TCP), После того, как ресурс сокета создан, он может быть уничтожен с помощью функции socket_close(), имеющей следующий синтаксис:
socket_close($socket);
Ошибки сокетов
socket_last_error($socket);
socket_strerror($error_code);
Создание клиентских сокетов.
Создание сокета, готового для подключения к другому сокету в Internet, выполняется с помощью функции socket_connect ():
$socket — это сокет для записи данных;
Когда эта функция выполняется, она отправляет содержимое буфера через подключенный сокет и возвращает количество записанных байт, либо булевское значение false в случае возникновения ошибки.
Для чтения данных из сокета применяется функция socket_read<) со следующим синтаксисом:
Константы типа для socket_read()
Константа | Описание |
---|---|
PHP_BINARY_READ | �?нтерпретировать данные как бинарные (поумолчанию). |
PHP_NORMAL_READ | Читать данные заданной длины, либо пока не встретится символ новой строки (\r или \n). |
В листинге ниже представлен пример использования сокетов для извлечения индексной страницы Web-сайта, в него включено все, что рассматривалось выше. �?звлечение индексной страницы осуществляется отправкой простого GET-запроса HTTP 1.0 с последующим чтением результата в переменную.
Создание серверных сокетов
Когда осуществляется привязка к адресу, убедитесь, что ваш сокет не допустит подключений к чему-либо другому, кроме указанного адреса и порта! Это означает, что привязка сокета к локальному хосту (127.0.0.1) позволит вашему сокету принимать топько покальные подключения.
Второй шаг: настроить сокет на прослушивание трафика на предмет попыток подключения к нему. Это делается с помощью функции socket_listen():
Третий и последний шаг в создании серверного сокета — дать команду на прием входящих подключений. Это делается функцией socket_accept ():
socket_accept($socket);
В листинге ниже создается простой сокетный сервер, принимающий единственное подключение, максимум 1024 байта входного потока и отображающий этот поток пользователю.
Создание простого сервера на основе сокета
У меня на компе этот скрипт лежит в папке денвера по пути: C:\WebServers\home\app.loc\www\sockets\test.php
Теперь, если запустить наш скрипт из командной строки таким макаром: C:\WebServers\home\app.loc\www\sockets>php test.php В командной строке мы увидим следующее:
Сервер в ответ пришлёт нам наш же запрос + заголовки:
В командной строке мы увидим новые данные
Чтобы создать сервер, сокеты которого ведут прослушивание на портах с номерами ниже 1000, данный пользователь должен иметь в системе административные права. Также следует отметить, что приведенный выше сценарий не завершит работу до тех пор, пока не будет установлено соединение, что может создать впечатление «зависания».
Одновременная работа с несколькими сокетами
В листинге, на предыдущей странице был представлен сервер на базе сокетов. Однако он не слишком удобен для реальных, целей, поскольку к нему может выполняться только одно подключение одновременно. Чтобы создать более удобный сервер сокетов, необходимо научиться работать одновременно с множеством сокетов. Чтобы сделать это, понадобится функция socket_select (), синтаксис которой выглядит следующим образом:
В случае ошибочного завершения socket_select() возвращает булевское значение false.
Для использования этой функции в реальном приложении первым делом должен быть создан сокет, представляющий сервер в целом. Этот главный сокет будет привязан к определенному адресу и порту и начнет прослушивание подключений.
Этот новый подключенный сокет затем подвергается мониторингу через тот же вызов socket_select() (за счет добавления его к тому же массиву, куда уже добавлен наш главный сокет) и реализуется логика приложения, обеспечивающая функциональность нашего сервера. В листинге ниже представлен работающий пример простого сервера, принимающего настраиваемое число подключений.
Создание многосортного сервера на РНР
В листинге выше вскрыты некоторые ограничения сценарного механизма РНР, которые требуют несколько более сложного обходного пути в форме вызова socket_select():
$NULL = NULL;
�? последующей ее передачи функции socket_select().
hharek
В этой статье речь пойдёт о протоколе WebSocket, немного теории и реализация его в браузере через JavaScript и на сервере на «голом» php.
Оглавление
Общая информация о веб-сокетах
Веб-сокеты, это такая технология, которая позволяет браузеру и серверу создать одно постоянное соединение и через него обмениваться данными. Преимущества такого подхода в том что для отслеживания изменения на сайте, браузеру теперь нет необходимости постоянно «сыпать» запросы на сервер. При постоянном соединении сервер теперь может когда ему надо отправить сообщение браузеру, т.е. связь двунаправленная, от браузера к серверу и от сервера к браузеру.
Реализация клиента на Javascript
Протокол веб-сокет создан уже давно (приобрёл статус RFC в 11.12.2011) и поддерживается большинством браузеров. Чтобы узнать поддерживает ли ваш браузер веб-сокеты перейдите по ссылке.
Работа в браузерах с вебсокетам проходит в несколько этапов:
Тестировать веб-сокеты мы будем на сервере websocket.org «ws://echo.websocket.org», который будет принимать от нас сообщения и отвечать на них повторением сообщением. Этот сайт как раз существует, что лучше понять веб-сокеты, он понимает кросс-доменные запросы, поэтому страницу с JavaScript будем размещать у себя на локальном компьютере.
Этап. Рукопожатие
Чтобы создать соединение по веб-сокету достаточно создать объект WebSocket, в котором указывается урл для подключения.
Используйте протокол «ws://», если нужно не шифрованное соединение или протокол «wss://» для шифрованного соединения.
Этап. Создание обработчиков событий.
Этап. Отправка сообщений на сервер
По веб-сокету сообщения отправляются в виде строки. Пример отправки простого текстового сообщения.
Обработка приходящих данных лежит уже на стороне сервера. Чаще для удобства работы по вебсокету отправляют JSON данные серилизованные в строку и обрабатывают приходящие данные как строка в JSON-e. Пример использования:
Удобный способ отправки сообщений по веб-сокету служит протокол «JSON-RPC» (ссылка). Это очень простой протокол, который облегчит взаимодействие браузера и сервера. Пример использования JSON RPC:
Параметры json-rpc объекта:
Чтобы закрыть соединение используем метод close().
Протокол WebSocket
Рукопожатие. Запрос браузера
в котором обязательно должы присутствовать эти заголовки:
Также есть дополнительные заголовки:
Рукопожатие. Ответ сервера
Для начала сообщим браузеру, что сервер понимает WebSocket-протокол. Для это отправим ответное сообщение:
В сообщении символ новой строки должен быть в Windows-стиле (\r\n), а в конце сообщения должно быть две новые строки (\r\n\r\n). Заголовок «Sec-WebSocket-Accept» вычисляется в зависимости от заголовка «Sec-WebSocket-Key» присланного браузером, порядок получения его таков:
В PHP вычисление «Sec-WebSocket-Accept» будет выглядит так:
Отправка сообщений
После того как браузер получает ответ, устанавливается постоянное TCP-соединение и обмен сообщениями между сервером и браузером осуществляется по бинарному протоколу ничего общего с HTTP не имеющего. Бинарные сообщения, которые пересылаются по этому протоколу именуют ещё «фреймами» (frame).
Формат фрейма по 32 бита, как в RFC.
Формат фрейма по 16 бит.
Разберём первые 16 бит фрейма (далее заголовок фрейма):
Длина сообщения указывается в байтах и вычисляется по схеме (ну и намудрили):
Маска используется для того чтобы замаскировать сообщение. Маска используется для защиты от атаки отравленый кэш.
Фрейм может быть замаскирован, а может быть и нет:
Если фрейм замаскирован, то флаг маски установливается в 1 и следующие 32 бита (4 байта) будет маска, а строка сообщения будет XOR закодировано. Это значит что над каждым байтом в сообщении будет выполнено побитовая операция «исключающее или» с байтом из маски. В PHP для это используется символ «^». Работает это примерно так «z» ^ «m» ^ «m» === «z». Пример на PHP:
Текст сообщения должен быть в кодировке UTF-8.
Реализация сервера на PHP
Исходники простого WebSocket echo-сервера выложил сюда. Код хорошо документирован, но я всё же опишу некоторые тонкости реализации. Чтобы «поднять» WebSocket сервер нужно создать обычный TCP-сервер. В PHP TCP-сервер реализуется через «stream_socket» или через PHP расширение «sockets». Различия между ними в том, что «stream_socket» работает на встроенных функциях PHP для работы с потоками, «sockets» же работает через модуль PHP и повторяет функции для работы с сокетами в языке « C». Я выбрал «sockets».
Процесс реализован через «while» с задержкой 0.2 секунды. Процесс не форкается и сообщения выбрасывает в консоль, поэтому запускать необходимо только через консоль. Для того, чтобы обслуживать несколько клиентов одновременно, сокет делаю неблокирующим и через «socket_select» каждые 0.2 секунды прослушиваю сокет. При рукопожатии проверяю только наличие заголовков.
Фреймы парсю через «pack/unpack». Сервер не понимает фрагментированных фреймов. Сервер выдаёт только незамаскированные сообщения, т.к. некоторые браузеры не понимают замаскированных сообщений. Сервер реагирует только на текстовые фреймы и фрейм закрытия соединения, бинарные фреймы не понимает.
Ну собственно всё, удачи в исследовании этого не простого протокола.
Примеры
Пример #1 Пример использования сокетов: Простой сервер TCP/IP
Этот пример показывает работу простого сервера. Измените переменные address и port в соответствии с вашими настройками и выполните. Затем вы можете соединиться с сервером с командой, похожей на: telnet 192.168.1.53 10000 (где адрес и порт должны соответствовать вашим настройкам). Всё, что вы наберёте на клавиатуре, будет затем выведено на сервере и отправлено вам обратно. Для отключения наберите ‘выход’.
/* Позволяет скрипту ожидать соединения бесконечно. */
set_time_limit ( 0 );
/* Включает скрытое очищение вывода так, что мы видим данные
* как только они появляются. */
ob_implicit_flush ();
$address = ‘192.168.1.53’ ;
$port = 10000 ;
Пример #2 Пример использования сокетов: Простой клиент TCP/IP
Этот пример показывает использование простого одноразового HTTP-клиента. Он просто соединяется со страницей, отправляет запрос HEAD, выводит ответ и завершает работу.
Соединение TCP/IP
/* Получаем IP-адрес целевого хоста. */
$address = gethostbyname ( ‘www.example.com’ );
User Contributed Notes 2 notes
You can easily extend the first example to handle any number of connections instead of jsut one
#!/usr/bin/env php
( E_ALL );
/* Permitir al script esperar para conexiones. */
set_time_limit ( 0 );
/* Activar el volcado de salida implícito, así veremos lo que estamo obteniendo
* mientras llega. */
ob_implicit_flush ();
$address = ‘127.0.0.1’ ;
$port = 10000 ;
//clients array
$clients = array();