Функция PHP socket_get_status(): всё, что нужно знать
Узнайте, как использовать функцию socket_get_status() в PHP для проверки состояния потокового соединения, обнаружения таймаутов и анализа буфера.
Когда вы читаете данные из сетевого соединения в PHP или записываете в него, вам нередко нужно знать, активно ли соединение, не истёк ли таймаут чтения и сколько байт ожидает в буфере. Функция socket_get_status() отвечает именно на эти вопросы: она возвращает снимок текущего состояния потока.
В этой главе объясняется, что возвращает socket_get_status(), когда важен каждый из полей, и как безопасно использовать функцию с таймаутами и неблокирующими потоками.
Что такое функция socket_get_status()?
socket_get_status() возвращает текущее состояние потокового ресурса в виде ассоциативного массива. По сути это псевдоним stream_get_meta_data(), поэтому обе функции возвращают одинаковые данные — socket_get_status() просто историческое название, сохранённое для сетевого кода.
Несмотря на название, функция предназначена для ресурсов типа stream — таких, которые возвращают fsockopen(), pfsockopen() или stream_socket_client() — но не для низкоуровневых ресурсов сокетов, созданных через socket_create(). Передача неправильного типа ресурса может вызвать предупреждения об устаревании в PHP 8+ и не даст ожидаемых данных.
Как использовать функцию socket_get_status()
Использовать функцию socket_get_status() несложно. Вот её синтаксис:
Синтаксис PHP функции socket_get_status()
socket_get_status(resource $stream): arrayФункция принимает один параметр:
$stream: Потоковый ресурс, состояние которого нужно получить.
Она возвращает ассоциативный массив. Наиболее полезные ключи:
| Ключ | Тип | Значение |
|---|---|---|
timed_out | bool | true, если последняя операция чтения/записи превысила таймаут потока. |
blocked | bool | true, если поток находится в блокирующем режиме. |
eof | bool | true, если достигнут конец потока (соединение закрыто). |
unread_bytes | int | Байты, уже считанные во внутренний буфер PHP, но ещё не обработанные. |
stream_type | string | Используемый транспорт, например tcp_socket/ssl. |
wrapper_type | string | Обёртка, обрабатывающая поток, например http. |
mode | string | Режим доступа, с которым был открыт поток, например r+. |
Проверка активности соединения
Вот минимальный пример, открывающий TCP-поток, читающий строку и использующий socket_get_status() для определения, закрыл ли сервер соединение:
Как использовать функцию socket_get_status()?
<?php
$stream = fsockopen("tcp://www.example.com", 80, $errno, $errstr, 30);
if (!$stream) {
die("Error: $errstr ($errno)");
}
fwrite($stream, "GET / HTTP/1.0\r\nHost: www.example.com\r\n\r\n");
$line = fgets($stream);
$status = socket_get_status($stream);
echo $status["eof"] ? "Connection closed by server\n" : "Connection still open\n";
fclose($stream);
?>Мы используем fsockopen() для создания потока и проверяем успешность его создания перед дальнейшими действиями. После отправки запроса и чтения одной строки с помощью fgets() поле eof показывает, завершила ли удалённая сторона передачу. Всегда закрывайте поток с помощью fclose() после завершения работы.
Обнаружение таймаута чтения
Поле timed_out делает socket_get_status() по-настоящему полезной. Когда вы устанавливаете таймаут чтения с помощью stream_set_timeout() (или socket_set_timeout()), медленное чтение не генерирует исключение — fgets() просто возвращает false. Затем вы вызываете socket_get_status(), чтобы понять почему она вернула false:
<?php
$stream = fsockopen("tcp://www.example.com", 80, $errno, $errstr, 30);
if (!$stream) {
die("Error: $errstr ($errno)");
}
// Wait at most 2 seconds for data before giving up.
stream_set_timeout($stream, 2);
$line = fgets($stream); // We never sent a request, so nothing arrives.
$status = socket_get_status($stream);
if ($status["timed_out"]) {
echo "Read timed out\n";
} else {
echo "Got data\n";
}
fclose($stream);
?>Поскольку запрос не был отправлен, серверу нечего отвечать, срабатывает двухсекундный таймаут, и скрипт выводит Read timed out. Без socket_get_status() невозможно отличить таймаут от реального окончания потока — в обоих случаях fgets() возвращает false.
Когда использовать socket_get_status()
- После неудачного чтения. Возврат
falseизfgets()/fread()неоднозначен; проверьтеtimed_outиeof, чтобы понять причину. - Перед повторным использованием соединения. Проверьте
eof, чтобы убедиться, что keep-alive соединение по-прежнему активно перед отправкой следующего запроса. - С неблокирующими потоками. Когда вы вызываете
stream_set_blocking()для перевода сокета в неблокирующий режим,unread_bytesиblockedпомогают анализировать буферизованные данные.
Важные замечания
- Функция работает только с ресурсами stream — не с ресурсами
socket_create(). Для последних используйтеsocket_last_error()/socket_get_option(). socket_get_status()отражает состояние буфера и таймаутов на момент вызова; она не проверяет сеть активно, поэтому значениеeofравноеfalseне гарантирует доступность удалённого узла.- Сбросьте флаг таймаута, выполнив ещё одно успешное чтение — массив пересчитывается при каждом вызове.
Заключение
Функция socket_get_status() — лёгкий способ проверить состояние потокового сокета в PHP. Наиболее ценное поле — timed_out, позволяющее отличить таймаут чтения от закрытого соединения — то, что одно лишь возвращаемое значение fgets() не позволяет сделать. В сочетании с eof, unread_bytes и средствами управления таймаутом и блокировкой из stream_set_timeout() и stream_set_blocking() функция даёт всю необходимую информацию для надёжной работы с сетевыми соединениями.