W3docs

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

Функция pfsockopen() открывает постоянное соединение через сокет. Узнайте о её синтаксисе, параметрах и современной альтернативе на PHP 8.2+.

Функция pfsockopen() открывает постоянное соединение через интернет-сокет или сокет Unix-домена. «Постоянное» означает, что базовый сокет остаётся открытым после завершения скрипта и повторно используется последующими запросами, подключающимися к тому же хосту и порту — это позволяет избежать накладных расходов на открытие нового соединения (поиск DNS, TCP-рукопожатие) каждый раз.

Важно: pfsockopen() была объявлена устаревшей в PHP 8.1 и удалена в PHP 8.2, поэтому она недоступна в современном PHP. В этой главе описывается её историческое поведение и показано, что использовать вместо неё. Если вы используете PHP 8.2+, перейдите к разделу Современная альтернатива.

В этой главе рассматривается, что делала pfsockopen(), её синтаксис и параметры, пример HTTP-запроса, разница между постоянными и непостоянными сокетами, типичные ловушки и рекомендуемая замена.

Что такое функция pfsockopen()?

pfsockopen()persistent fsockopen») — это постоянный аналог fsockopen(). Обе функции открывают низкоуровневый сокет и возвращают поток, из которого можно читать и в который можно писать с помощью стандартных файловых функций. Единственное отличие: соединение, открытое через fsockopen(), закрывается в конце запроса, тогда как соединение, открытое через pfsockopen(), помещается в пул процессом PHP и повторно используется при последующих запросах к тому же адресу.

Это делало её полезной в долгоживущих конфигурациях (FPM/mod_php), где один и тот же скрипт многократно обращался к одному и тому же бэкенду — например к узлу memcached или пользовательскому TCP-сервису — и было целесообразно избегать накладных расходов рукопожатия при каждом запросе.

Как использовать функцию pfsockopen()

Ниже приведён синтаксис функции:

Синтаксис PHP функции pfsockopen()

pfsockopen($hostname, $port, &$errno, &$errstr, $timeout);

Функция принимает пять параметров:

  • $hostname: имя хоста или IP-адрес сервера для подключения.
  • $port: номер порта для подключения.
  • $errno: переменная, передаваемая по ссылке, в которую будет записан код ошибки в случае её возникновения.
  • $errstr: переменная, передаваемая по ссылке, в которую будет записано сообщение об ошибке в случае её возникновения.
  • $timeout: период ожидания в секундах.

Обязательны только $hostname и $port; параметры $errno, $errstr и $timeout являются необязательными, но их почти всегда стоит передавать, чтобы иметь возможность диагностировать сбои.

Возвращаемое значение: возвращает поток/ресурс в случае успеха или false в случае ошибки.

Ниже приведён пример использования функции pfsockopen() для создания постоянного соединения через сокет, обработки ошибок и обмена данными:

Как использовать функцию pfsockopen()?

<?php

$hostname = "example.com";
$port = 80;
$errno = 0;
$errstr = "";
$timeout = 30;

$socket = pfsockopen($hostname, $port, $errno, $errstr, $timeout);

if ($socket === false) {
    echo "Connection failed: $errno - $errstr";
} else {
    fwrite($socket, "GET / HTTP/1.1\r\nHost: $hostname\r\n\r\n");
    $response = fread($socket, 2048);
    echo $response;
    fclose($socket);
}
?>

Здесь мы открываем соединение с example.com на порту 80 с таймаутом 30 секунд. При сбое код и сообщение об ошибке записываются в $errno/$errstr. После подключения мы отправляем сырой HTTP-запрос с помощью fwrite(), читаем ответ через fread() и закрываем дескриптор функцией fclose().

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

Постоянные и непостоянные сокеты

В таблице ниже показано, когда важно каждое из поведений:

Аспектfsockopen()pfsockopen()
Время жизни соединенияЗакрывается в конце запросаПомещается в пул и повторно используется между запросами
Стоимость первого запросаПолное рукопожатие при каждом запросеПолное рукопожатие только при первом запросе
Лучше подходит дляЕдиничных или коротких скриптовМногократных обращений к одному бэкенду в долгоживущих воркерах

Постоянство полезно только тогда, когда один и тот же процесс обслуживает множество запросов к одному и тому же хосту/порту — как правило, в PHP-FPM или mod_php. В CLI-скриптах, которые немедленно завершаются, переиспользовать нечего, поэтому pfsockopen() ведёт себя как fsockopen().

Типичные ловушки

  • fclose() на самом деле не закрывает соединение. Вызов fclose() на постоянном сокете лишь возвращает его в пул. В этом и заключается смысл — но это означает, что наполовину сломанное соединение может быть передано следующему запросу. Всегда проверяйте соединение (или отправляйте лёгкий ping), прежде чем полагаться на него.
  • Нет мультиплексирования. Каждый воркер PHP поддерживает собственный пул, поэтому количество постоянных сокетов растёт вместе с числом воркеров. На нагруженном сервере это может исчерпать лимит соединений бэкенда.
  • Утечки состояния. Поскольку сокет существует между запросами, оставшиеся в буфере данные или промежуточное состояние протокола от предыдущего запроса могут испортить следующий. Постоянные сокеты лучше всего подходят для простых протоколов типа «запрос — ответ» без сохранения состояния.
  • Удалена в PHP 8.2. Код, по-прежнему вызывающий pfsockopen(), вызовет фатальную ошибку Call to undefined function в PHP 8.2+.

Современная альтернатива

В PHP 8.2+ используйте stream_socket_client() с флагом STREAM_CLIENT_PERSISTENT. Она более гибкая (поддерживает транспорты tcp://, tls:// и unix://, а также контексты потоков) и является активно поддерживаемым API:

<?php

$socket = stream_socket_client(
    "tcp://example.com:80",
    $errno,
    $errstr,
    30,
    STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT
);

if ($socket === false) {
    echo "Connection failed: $errno - $errstr";
} else {
    fwrite($socket, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
    echo fread($socket, 2048);
    fclose($socket);
}
?>

Для обычной работы с HTTP лучшим выбором, как правило, является высокоуровневый клиент — cURL или Guzzle; к сырым сокетам стоит обращаться лишь тогда, когда требуется пользовательский протокол.

Заключение

pfsockopen() создавала постоянное, повторно используемое соединение через сокет — удобное для долгоживущих PHP-воркеров, которые многократно обращались к одному и тому же бэкенду. Её ключевая особенность состояла в том, что fclose() возвращала сокет в пул, а не закрывала его. Поскольку функция была удалена в PHP 8.2, в новом коде следует использовать stream_socket_client() с флагом STREAM_CLIENT_PERSISTENT. Для изучения связанного ввода-вывода потоков см. fsockopen(), fwrite() и fread().

Practice

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