W3docs

Функция PHP socket_set_blocking(): всё, что нужно знать

Узнайте, как управлять блокирующим и неблокирующим режимами сокетов в PHP с помощью socket_set_blocking() и её актуальных замен.

При написании сетевого кода на PHP часто требуется управлять тем, ожидает ли вызов ввода-вывода данных (блокирующий режим) или возвращается немедленно (неблокирующий режим). Функция socket_set_blocking() исторически использовалась как псевдоним для установки этого режима. В PHP 8.1 она была объявлена устаревшей, и использовать её в новом коде не следует.

Важный нюанс: правильная замена зависит от типа соединения, и именно здесь многие руководства допускают ошибку:

  • Если вы использовали расширение Sockets (socket_create(), возвращающее объект Socket), применяйте socket_set_nonblock() и socket_set_block().
  • Если вы использовали поток (fsockopen() или stream_socket_client(), возвращающие потоковый ресурс), применяйте stream_set_blocking().

socket_set_blocking() фактически являлась псевдонимом stream_set_blocking(), поэтому она всегда работала только с потоковыми ресурсами — но не с объектами Socket. Именно это различие позволяет избежать ошибки TypeError во время выполнения.

Блокирующий и неблокирующий режим

В блокирующем режиме (по умолчанию) вызов чтения или записи приостанавливает выполнение скрипта до завершения операции. Вызов socket_read() на пустом сокете просто ждёт поступления байтов.

В неблокирующем режиме тот же вызов возвращается немедленно. Если данные ещё не готовы, он возвращает пустой результат (или false) вместо ожидания. Это позволяет одному скрипту обслуживать множество соединений или сохранять отзывчивость — ценой необходимости опрашивать состояние и проверять, действительно ли поступили данные.

Используйте блокирующий режим, когда…Используйте неблокирующий режим, когда…
Вы обрабатываете одно соединение за разВы обслуживаете множество клиентов в одном цикле
Простота важнее пропускной способностиСкрипт должен оставаться отзывчивым
Короткий, предсказуемый запрос/ответВы реализуете собственный цикл опроса/событий

Установка режима для объекта Socket

Если соединение создано с помощью расширения Sockets, используйте socket_set_nonblock() и socket_set_block():

socket_set_nonblock(Socket $socket): bool
socket_set_block(Socket $socket): bool

Обе функции возвращают true при успехе и false при ошибке. Полный, работоспособный пример с обработкой ошибок и освобождением ресурсов:

<?php

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    die("Socket creation failed: " . socket_strerror(socket_last_error()) . "\n");
}

// Switch to non-blocking mode
if (!socket_set_nonblock($socket)) {
    die("Failed to set non-blocking mode\n");
}
echo "Socket is now non-blocking.\n";

// ... perform non-blocking socket operations ...

// Switch back to blocking mode if needed
socket_set_block($socket);
echo "Socket is now blocking again.\n";

socket_close($socket);

Не вызывайте stream_set_blocking($socket, false) для объекта Socketsocket_create() возвращает экземпляр Socket, а stream_set_blocking() принимает только потоковый ресурс. Передача объекта Socket вызовет TypeError.

Установка режима для потока

Если соединение открыто с помощью fsockopen() или stream_socket_client(), у вас есть потоковый ресурс, и следует использовать stream_set_blocking():

stream_set_blocking(resource $stream, bool $enable): bool
  • $stream: настраиваемый потоковый ресурс.
  • $enable: true для блокирующего режима, false для неблокирующего.
<?php

$stream = stream_socket_client('tcp://example.com:80', $errno, $errstr, 5);
if ($stream === false) {
    die("Connect failed: $errstr ($errno)\n");
}

// Switch the stream to non-blocking mode
if (!stream_set_blocking($stream, false)) {
    die("Failed to set non-blocking mode\n");
}
echo "Stream is now non-blocking.\n";

fclose($stream);

Цикл неблокирующего чтения

Неблокирующий режим полезен только при наличии опроса. Типичный паттерн отправляет запрос, а затем многократно проверяет ответ без заморозки скрипта:

<?php

$stream = stream_socket_client('tcp://example.com:80', $errno, $errstr, 5);
if ($stream === false) {
    die("Connect failed: $errstr ($errno)\n");
}

stream_set_blocking($stream, false);
fwrite($stream, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");

$response = '';
$start = time();
while (!feof($stream) && time() - $start < 5) {
    $chunk = fread($stream, 8192);
    if ($chunk === '' || $chunk === false) {
        // No data yet — do other work or wait briefly
        usleep(50000); // 50 ms
        continue;
    }
    $response .= $chunk;
}

echo substr($response, 0, 15), "\n"; // e.g. "HTTP/1.1 200 OK"
fclose($stream);

Вызов usleep() предотвращает интенсивный цикл опроса, нагружающий процессор. В продакшене этот ручной опрос обычно заменяют на stream_select() для эффективного ожидания данных сразу на нескольких потоках.

Примечания о версиях

  • PHP 8.1: функция socket_set_blocking() объявлена устаревшей. Её вызов генерирует уведомление об устаревании.
  • Функция являлась псевдонимом stream_set_blocking(), поэтому никогда не принимала объекты Socket.
  • Для объектов Socket функции socket_set_nonblock() / socket_set_block() всегда были правильным вариантом и по-прежнему поддерживаются.

Связанные функции

  • socket_get_status() — получить состояние сокета, в том числе информацию о блокировке.
  • socket_set_timeout() — управлять временем ожидания блокирующей операции перед прерыванием.
  • PHP Streams — более широкий API потоков, частью которого является stream_set_blocking().

Заключение

Управление режимом блокировки соединения необходимо для создания отзывчивых сетевых приложений на PHP. Главное — использовать функцию, соответствующую типу соединения: socket_set_nonblock() / socket_set_block() для объектов Socket и stream_set_blocking() для потоковых ресурсов. Избегайте устаревшей socket_set_blocking() в новом коде и помните, что неблокирующий режим оправдывает себя только в сочетании с циклом опроса.

Практика

Практика
Что делает функция socket_set_blocking() в PHP?
Что делает функция socket_set_blocking() в PHP?
Was this page helpful?