PHP: Программирование сокетов - Одновременная работа с несколькими сокетами
| Индекс материала |
|---|
| PHP: Программирование сокетов |
| Создание клиентских сокетов. |
| Создание серверных сокетов |
| Одновременная работа с несколькими сокетами |
| Все страницы |
Одновременная работа с несколькими сокетами
В листинге, на предыдущей странице был представлен сервер на базе сокетов. Однако он не слишком удобен для реальных, целей, поскольку к нему может выполняться только одно подключение одновременно. Чтобы создать более удобный сервер сокетов, необходимо научиться работать одновременно с множеством сокетов. Чтобы сделать это, понадобится функция socket_select (), синтаксис которой выглядит следующим образом:
socket_select(&$read, &$write, &$error, $sec [, $usec]);
Здесь $read, $write и $error — переданные по ссылке переменные (точнее, массивы). Эти массивы должны содержать список всех сокетов, за которыми нужно наблюдать на предмет чтения, записи и перехвата ошибок соответственно. Например, помещение активного сокета в массив, передаваемый в параметре $read, заставляет РНР проверять, есть ли в этом сокете данные для чтения. Последние два параметра - $sec и необязательный $usec - это значения тайм-аута, управляющие тем, как долго будет ожидать функция socket_select(), прежде чем вернуть управление РНР.
В результате выполнения функция socket_select() возвращает целое число, указывающее общее количество измененных сокетов ( из переданного списка), и модифицирует массивы $read, $write и $error, удаляя из них те элементы, которые не были изменены. В результате каждый из этих массивов будет содержать только список сокетов, отвечающих следующим требованиям:
Сокеты, перечисленные в массиве $read, содержат данные, подлежащие чтению из них, либо входящие подключения к ним.
Сокеты, перечисленные в массиве $write, содержат данные, подлежащие записи в них.
Сокеты, перечисленные в массиве $error, содержат ошибки, которые нужно обработать.
В случае ошибочного завершения socket_select() возвращает булевское значение false.
Для использования этой функции в реальном приложении первым делом должен быть создан сокет, представляющий сервер в целом. Этот главный сокет будет привязан к определенному адресу и порту и начнет прослушивание подключений.
Этот сокет будет также добавлен в массив $read и будет запущен управляемый бесконечный цикл. Затем с помощью функции socket_select() будет организован мониторинг главного сокета на предмет новых подключений. Когда появляется новое подключение, автоматически вызывается функция socket_accept(), что приводит к созданию нового серверного сокета, используемого для взаимодействия с подключенным клиентом.
Этот новый подключенный сокет затем подвергается мониторингу через тот же вызов socket_select() (за счет добавления его к тому же массиву, куда уже добавлен наш главный сокет) и реализуется логика приложения, обеспечивающая функциональность нашего сервера. В листинге ниже представлен работающий пример простого сервера, принимающего настраиваемое число подключений.
Создание многосортного сервера на РНР
<?php
set_time_limit(0);
$NULL = NULL;
$address = "127.0.0.1";
$port = 4545;
$max_clients = 10;
$client_sockets = array();
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$res = true;
$res &= @socket_bind($master, $address, $port);
$res &= @socket_listen($master);
if(!$res)
{
die ("Невозможно привязать и прослушивать $address: $port\n");
}
$abort = false;
$read = array($master);
while(!$abort)
{
$num_changed = socket_select($read, $NULL, $NULL, 0, 10);
/* Изменилось что-нибудь? */
if ($num_changed)
{
/* Изменился ли главный сокет (новое подключение) */
if(in_array($master, $read))
{
if(count($client_sockets) < $max_clients)
{
$client_sockets[]= socket_accept($master);
echo "Принято подключение (" . count($client_sockets) . " of $max clients)\n";
}
}
/* Цикл по всем клиентам с проверкой изменений в каждом из них */
foreach($client_sockets as $key => $client)
{
/* Новые данные в клиентском сокете? Прочитать и ответить */
if(in_array($client, $read))
{
$input = socket_read($client, 1024);
if($input === false)
{
socket_shutdown($client);
unset($client_sockets[$key]);
}
else
{
$input = trim($input);
if (!@socket_write($client, "Вы сказали: $input\n") )
{
socket_close($client);
unset ( $client_sockets[$key] ) ;
}
}
if($input == 'exit')
{
socket_shutdown($master);
$abort = true;
}
}// END IF in_array
} // END FOREACH
} // END IF ($num_changed)
$read = $client_sockets;
$read[] = $master;
} // END WHILE
?>
Кстати!
В листинге выше вскрыты некоторые ограничения сценарного механизма РНР, которые требуют несколько более сложного обходного пути в форме вызова socket_select():
$num_changed = socket_select($read, $NULL, $NULL, 0, 10);
Обратите внимание на применение переменной с именем $NULL. В РНР для функций, принимающих параметры по ссылке (как это и делает socket_select() в первом приближении), NULL является недопустимым значением. Однако передача NULL в качестве одного или более параметров-списков вполне корректна. Поэтому обходной маневр заключается в присвоении переменной $NULL значения NULL:
$NULL = NULL;
И последующей ее передачи функции socket_select().
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.
PHP: Программирование сокетов - Одновременная работа с несколькими сокетами
