W3docs

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

Узнайте, как использовать socket_set_timeout() в PHP для установки таймаута на операции чтения/записи в потоке и избежания зависания скрипта.

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

Что делает функция socket_set_timeout()

socket_set_timeout() устанавливает таймаут для операций ввода/выводаfread(), fgets(), fwrite() и т. д. — для потока, открытого с помощью fsockopen() или pfsockopen(). Если операция чтения или записи не завершается в установленный срок, она возвращается досрочно, а поток помечается как тайм-аут.

Два действия, которые она не выполняет:

  • Она не влияет на таймаут соединения. Это пятый аргумент функции fsockopen($host, $port, $errno, $errstr, $connectTimeout). socket_set_timeout() управляет только передачей данных после того, как соединение установлено.
  • Она не завершает вызов с явной ошибкой. Операция чтения, при которой произошёл таймаут, возвращает данные, полученные к тому моменту (часто пустую строку), и устанавливает флаг — вы должны проверить этот флаг самостоятельно с помощью stream_get_meta_data().

Ловушка с именованием: несмотря на префикс socket_, эта функция относится к семейству потоков, а не к расширению Sockets. Она работает с ресурсами от fsockopen(), но никогда с ресурсами от socket_create(). Начиная с PHP 8.0 она является устаревшим псевдонимом stream_set_timeout() — в новом коде используйте это имя.

Синтаксис

socket_set_timeout(resource $stream, int $seconds, int $microseconds = 0): bool
ПараметрОписание
$streamОткрытый ресурс потока, возвращённый функцией fsockopen() или pfsockopen().
$secondsТаймаут в целых секундах.
$microsecondsДополнительное время в микросекундах (необязательно, по умолчанию 0).

Возвращает true в случае успеха и false при ошибке (например, если $stream не является допустимым ресурсом потока).

Рабочий пример

Откройте соединение, установите таймаут чтения в 5 секунд, затем проверьте, произошёл ли таймаут при чтении:

<?php

// Open a TCP connection to a web server.
$stream = fsockopen("www.example.com", 80, $errno, $errstr, 10);

if (!$stream) {
    echo "Connection failed: $errstr ($errno)\n";
    exit;
}

// Fail any single read/write that stalls for more than 5 seconds.
socket_set_timeout($stream, 5);

// Send a minimal HTTP request.
fwrite($stream, "GET / HTTP/1.0\r\nHost: www.example.com\r\n\r\n");

// Read the first line of the response.
$line = fgets($stream, 1024);

// Check whether that read hit the timeout.
$info = stream_get_meta_data($stream);

if ($info['timed_out']) {
    echo "Read timed out — the server was too slow.\n";
} else {
    echo "First response line: " . trim($line) . "\n";
}

fclose($stream);

Ключевым является вызов stream_get_meta_data(): элемент timed_out — это единственный надёжный способ отличить настоящий таймаут от соединения, которое просто закрылось.

Чтение в цикле

При чтении целого ответа проверяйте timed_out на каждой итерации, чтобы зависание в середине передачи не приводило к незаметному усечению данных:

<?php

$stream = fsockopen("www.example.com", 80, $errno, $errstr, 10);
socket_set_timeout($stream, 5);

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

$body = "";
while (!feof($stream)) {
    $chunk = fgets($stream, 4096);
    $info  = stream_get_meta_data($stream);

    if ($info['timed_out']) {
        echo "Stalled before the response finished.\n";
        break;
    }
    $body .= $chunk;
}

fclose($stream);
echo "Received " . strlen($body) . " bytes.\n";

Распространённые подводные камни

  • Неправильный тип ресурса. Передача ресурса от socket_create() не даёт полезного результата — используйте stream_set_timeout() для сокетов socket_create() или socket_set_option() для SO_RCVTIMEO/SO_SNDTIMEO.
  • Путаница между таймаутами соединения и чтения. Большой таймаут соединения fsockopen() не спасёт вас от медленного ответа; вам нужны оба.
  • Забыть проверить timed_out. Без этой проверки операция чтения с таймаутом выглядит точно так же, как чистое завершение потока, что приводит к незаметному усечению данных.

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

Заключение

socket_set_timeout() предотвращает зависание скрипта PHP из-за медленных операций чтения и записи в сеть. Помните, что она работает с потоками fsockopen() (а не с расширением Sockets), управляет вводом/выводом, а не соединением, и что для определения факта таймаута необходимо проверять флаг timed_out в результате stream_get_meta_data(). В новом коде используйте современное имя — stream_set_timeout().

Практика

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