Socket select: примеры (PHP)
socket_select(array &read, array &write, array &except, int tv_sec [, int tv_usec]): int|falseФункция socket_select в PHP предназначена для мониторинга массивов сокетов на предмет изменения их статуса. Она позволяет реализовать асинхронную обработку множества соединений в одном потоке, что особенно востребовано в серверных приложениях, например, в чат-серверах или игровых серверах.
read — массив сокетов, которые проверяются на доступность для чтения (поступление данных).
write — массив сокетов, проверяемых на готовность к записи (возможность отправки данных).
except — массив сокетов, проверяемых на наличие исключительных ситуаций (ошибки).
tv_sec — секундная часть таймаута ожидания изменений.
tv_usec — микросекундная часть таймаута (дополнение к секундам).
Возвращаемое значение: количество сокетов, изменивших статус, либо false при ошибке.
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8080);
$read = [$socket];
$write = $except = null;
$num_changed = socket_select($read, $write, $except, 5, 0);
if ($num_changed > 0) {
echo 'Данные доступны для чтения';
}
?>Данные доступны для чтения
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_nonblock($socket);
$write = [$socket];
$num_changed = socket_select(null, $write, null, 1);
if ($num_changed > 0) {
echo 'Сокет готов к отправке данных';
}
?>Сокет готов к отправке данных
stream_select — работает с потоковыми ресурсами (файлы, сокеты через stream_socket). Удобна при использовании потокового контекста.
socket_poll — низкоуровневая функция для опроса сокетов, обеспечивает более тонкий контроль, но требует ручного управления структурами данных.
Асинхронное расширение Swoole — предоставляет event loop для работы с тысячами соединений без блокировок, но требует установки дополнительного модуля.
import select
import socket
sock = socket.socket()
sock.connect(('127.0.0.1', 8080))
readable, writable, exceptional = select.select([sock], [], [], 5)
if readable:
print('Данные доступны')Данные доступны
const net = require('net');
const socket = net.createConnection(8080, '127.0.0.1');
socket.on('readable', () => {
console.log('Данные доступны');
});Данные доступны
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(socket_fd, &read_fds);
select(socket_fd+1, &read_fds, NULL, NULL, &timeout);Отличие PHP-версии: работа с объектами сокетов вместо файловых дескрипторов.
<?php
$num_changed = socket_select([], [], [], 1);
var_dump($num_changed);
?>Warning: socket_select(): No socket arrays provided in ... bool(false)
<?php
$read = [$socket];
$num_changed = socket_select($read, $write, $except, 1); // $write не определён
?>Warning: socket_select(): Unable to select [4]: Interrupted system call
<?php
$sockets = [$socket1, $socket2];
$read = $sockets;
socket_select($read, $write, $except, 1);
// $read теперь содержит только готовые сокеты
// $sockets остался неизменным
?>Важно: функция модифицирует переданные массивы, оставляя только изменившиеся сокеты.
PHP 8.0: строгая типизация параметров — аргументы tv_sec и tv_usec теперь принимаются как int|null вместо int.
PHP 7.1: добавлена возможность передачи null в таймаут для бесконечного ожидания.
До PHP 5.4: микросекундная часть таймаута имела максимальное значение 1 000 000, сейчас ограничение снято.
<?php
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 9999);
socket_listen($server);
$clients = [$server];
while (true) {
$read = $clients;
$write = $except = null;
if (socket_select($read, $write, $except, null) > 0) {
foreach ($read as $socket) {
if ($socket === $server) {
$new_client = socket_accept($server);
$clients[] = $new_client;
echo 'Новое подключение';
} else {
$data = socket_read($socket, 1024);
if ($data === '') {
socket_close($socket);
unset($clients[array_search($socket, $clients)]);
} else {
echo 'Получено: ' . $data;
}
}
}
}
}
?><?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$read = [$socket];
$start = microtime(true);
$result = socket_select($read, $write, $except, 1, 500000);
$end = microtime(true);
echo 'Ожидание: ' . ($end - $start) . ' секунд';
?>Ожидание: 1.500128 секунд
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'example.com', 80);
$except = [$socket];
// Имитация исключительной ситуации
socket_shutdown($socket, 2);
if (socket_select($read, $write, $except, 0) > 0) {
if (!empty($except)) {
echo 'Обнаружена исключительная ситуация';
}
}
?>Обнаружена исключительная ситуация