Socket сервер на php

Реализация многопоточного сервера на PHP

Данная публикация не претендует на полноту решения поставленного вопроса. Сервер разрабатывается исключительно в ознакомительных целях. Многие важные вопросы, такие как, например, обработка ошибок сокетов, опущены. Для реализации многопоточного сервера мы будем использовать, конечно же, потоки. Очень часто приходится видеть фразу, что, мол, в PHP потоков нет. Так вот это неправда. Потоки есть, но реализованы в отдельном расширении pthreads.

Для начала нам понадобится сборка PHP, скомпилированная с флагом thread safety. Я использую Windows для работы, поэтому скачал готовый пакет здесь. Нужно лишь правильно выбрать разрядность ОС, нужную версию PHP и, конечно же, Thread Safe версию. На протяжении статьи будет предполагаться, что архив с PHP мы распаковали в C:\php директорию. Далее нам нужно установить расширение pthreads. Идем сюда и выбираем версию, соответствующую скачанной версии PHP и разрядности системы. Из архива копируем файл php_pthreads.dll в директорию C:\php\ext и файл pthreadVC2.dll в директории C:\php и C:\Windows\System32. В директории C:\php переименовываем файл php.ini-development в php.ini и добавляем в него такую строку:

Также находим и раскоменчиваем директиву extension_dir и выставляем ей значение «C:\php\ext» (у меня в версии PHP7 относительные пути не заработали). Открываем командную строку и проверяем:

В конце первой строки вывода мы должны увидеть пометку (ZTS). Переходим непосредственно к реализации сервера. Создаем файл (в моём случае он будет располагаться по адресу C:\server.php. Для начала создадим сокет, который будет слушать порт 8080 на нашей локальной машине.

$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($server, '127.0.0.1', 8080); socket_listen($server);

Далее создаём пул воркеров.

$pool = new Pool(10, Worker::class);

Первый аргумент устанавливает максимальное количество действующих потоков, второй имя класса воркера. Для каких либо более определенных задач можно описать свой класс, унаследовав его от класса Worker. Мы же будем использовать оригинальный класс. Забегая вперед скажу, что в классе потока установленный воркер можно получить через $this->worker.

Далее реализуем класс, который будет выполняться в отдельном потоке. Класс должен наследоваться от Threaded.

class Task extends Threaded < protected $socket; public function __construct($socket) < $this->socket = $socket; > public function run() < if (!empty($this->socket)) < $response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello world!"; socket_write($this->socket, $response, strlen($response)); // при попытке закрытия сокета я получаю ошибку zend_mm_heap currupted, поэтому эту часть в тестовом решении опускаю //socket_close($this->socket); > > > 

Наш класс принимает в конструкторе сокет соединения с клиентом. Так же действия, выполняемые в потоке, должны быть описаны в методе run(). В моем случае это ответ клиенту базовых заголовков и текста «Hello world!».

Читайте также:  Php if line contains

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

 $servers = [$server]; while (true) < $read = $servers; if (socket_select($read, $write, $except, 0) >= 0 && in_array($server, $read)) < $task = new Task(socket_accept($server)); $pool->submit($task); > > 

Поскольку мы используем бесконечный цикл, я зарегистрирую функцию, которая выполнится при завершении работы скрипта и остановит работу пула. Функцию следует регистрировать до начала цикла.

 register_shutdown_function(function () use ($server, $pool) < if (!empty($server)) < socket_close($server); >$pool->shutdown(); >); 

Собственно всё. Запускаем сервер в командной строке и пробуем открыть в браузере localhost:8080.

cd C:\ C:\php\php.exe server.php

Ниже привожу полный код сервера.

 socket = $socket; > public function run() < if (!empty($this->socket)) < $response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello world!"; socket_write($this->socket, $response, strlen($response)); > > > register_shutdown_function(function () use ($server, $pool) < if (!empty($server)) < socket_close($server); >$pool->shutdown(); >); $servers = [$server]; while (true) < $read = $servers; if (socket_select($read, $write, $except, 0) >= 0 && in_array($server, $read)) < $task = new Task(socket_accept($server)); $pool->submit($task); > >

Источник

Сокеты: Сервер на PHP

Сокеты: Сервер на PHP

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

Сразу привожу код сервера на PHP с подробными комментариями:

header(‘Content-Type: text/plain;’); //Мы будем выводить простой текст
set_time_limit(0); //Скрипт должен работать постоянно
ob_implicit_flush(); //Все echo должны сразу же отправляться клиенту
$address = ‘localhost’; //Адрес работы сервера
$port = 1985; //Порт работы сервера (лучше какой-нибудь редкоиспользуемый)
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) //AF_INET - семейство протоколов
//SOCK_STREAM — тип сокета
//SOL_TCP — протокол
echo «Ошибка создания сокета»;
>
else echo «Сокет создан\n»;
>
//Связываем дескриптор сокета с указанным адресом и портом
if (($ret = socket_bind($sock, $address, $port)) < 0) echo "Ошибка связи сокета с адресом и портом";
>
else echo «Сокет успешно связан с адресом и портом\n»;
>
//Начинаем прослушивание сокета (максимум 5 одновременных соединений)
if (($ret = socket_listen($sock, 5)) < 0) echo "Ошибка при попытке прослушивания сокета";
>
else echo «Ждём подключение клиента\n»;
>
do //Принимаем соединение с сокетом
if (($msgsock = socket_accept($sock)) < 0) echo "Ошибка при старте соединений с сокетом";
> else echo «Сокет готов к приёму сообщений\n»;
>
$msg = «Hello!»; //Сообщение клиенту
echo «Сообщение от сервера: $msg»;
socket_write($msgsock, $msg, strlen($msg)); //Запись в сокет
//Бесконечный цикл ожидания клиентов
do echo ‘Сообщение от клиента: ‘;
if (false === ($buf = socket_read($msgsock, 1024))) echo «Ошибка при чтении сообщения от клиента»; >
else echo $buf.»\n»; //Сообщение от клиента
>
//Если клиент передал exit, то отключаем соединение
if ($buf == ‘exit’) socket_close($msgsock);
break 2;
>
if (!is_numeric($buf)) echo «Сообщение от сервера: передано НЕ число\n»;
else $buf = $buf * $buf;
echo «Сообщение от сервера: ($buf)\n»;
>
socket_write($msgsock, $buf, strlen($buf));
> while (true);
> while (true);
//Останавливаем работу с сокетом
if (isset($sock)) socket_close($sock);
echo «Сокет успешно закрыт»;
>
?>

Читайте также:  Сокеты в php пример

Данный код я постарался тщательно прокомментировать, поэтому, надеюсь, Вам в нём всё понятно. Главное понять следующее: сервер — это непрерывно выполняющийся скрипт, который просто ждёт подключения клиентов. Затем принимающий запрос и на основании этого запроса, возвращающий клиенту ответ. Это самое главное, что Вам необходимо понять.

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

Создано 16.01.2012 14:51:02

  • Михаил Русаков
  • Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

    Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
    Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

    Если Вы не хотите пропустить новые материалы на сайте,
    то Вы можете подписаться на обновления: Подписаться на обновления

    Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

    Порекомендуйте эту статью друзьям:

    Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

    1. Кнопка:
      Она выглядит вот так:
    2. Текстовая ссылка:
      Она выглядит вот так: Как создать свой сайт
    3. BB-код ссылки для форумов (например, можете поставить её в подписи):

    Комментарии ( 14 ):

    У меня ошибка, пишет что функции socket_create нет.

    Надо включить эту библиотеку. Для этого в php.ini расскоментируйте строчку: extension=php_sockets.dll, затем перезапустите сервер.

    Надо включить эту библиотеку. Для этого в php.ini расскоментируйте строчку: extension=php_sockets.dll, затем перезапустите сервер. — Вот я всё по инструкции сделал,а оно мне:
    Fatal error: Call to undefined function socket_create() in Z:\home\localhost\www\server.php on line 7

    Дополнительные модули к Денверу установите (скачайте их с http://denwer.ru).

    Читайте также:  Php подготовленные запросы like

    А что нужно именно скачать,можна ссылку ))

    Дополнительные модули нужно скачать: http://www.denwer.ru/packages/php5.html

    Ваш совет помог мне включить поддержку сокетов на денвере,спасибо!

    После установки дополнительных модулей ошибка сохранилась.

    Все отлично, но вот есть вопрос: здесь если мы получили коннект клиента, то пока он не перестанет слать что либо серверу(здесь пока не пошлет exit), другие клиенты будут как бы в очереди и не будут обрабатываться. Есть варианты одновременной обработки нескольких клиентов?

    Скажите хоть. Надо ли запускать самостоятельно «server.php» или его просто надо разместить и он сам будет непрерывно работать! Опишите пожалуйста, Может из за денвера моего не х..на не работает!?

    Надо запускать и сам он непрерывно работать не будет.

    «Главное понять следующее: сервер — это непрерывно выполняющийся скрипт» Зачем тогда вообще эти сокеты нужны? Где они используются?

    Скажите пожалуйста, а как его запускать. этот сокет сервер на Денвере?

    Для добавления комментариев надо войти в систему.
    Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.

    Copyright © 2010-2023 Русаков Михаил Юрьевич. Все права защищены.

    Источник

    Оцените статью