Socket create pair: примеры (PHP)

Создание парных сокетов с помощью socket_create_pair в PHP
Раздел: Сокеты
socket_create_pair(int domain, int type, int protocol, array &fd): bool

Функция socket_create_pair создает пару соединенных между собой сокетов, которые могут использоваться для межпроцессного взаимодействия (IPC) в рамках одного компьютера. Созданные сокеты являются неименованными и работают по принципу двусторонней связи (дуплекс).

Область применения

Функция применяется в ситуациях, когда необходимо организовать обмен данными между родственными процессами, например, между родительским и дочерним процессом после вызова pcntl_fork(). Это эффективная альтернатива использованию пайпов (pipes) или других механизмов IPC.

Аргументы функции

Сигнатура функции: socket_create_pair(int $domain, int $type, int $protocol, array &$pair): bool

  • $domain (обязательный): Семейство протоколов. Чаще всего используется AF_UNIX для коммуникации в пределах одной системы или AF_INET.
  • $type (обязательный): Тип сокета. Например, SOCK_STREAM (надежный, с установкой соединения) или SOCK_DGRAM (датаграммы, без установки соединения).
  • $protocol (обязательный): Конкретный протокол в рамках указанного домена. Обычно 0, что позволяет системе выбрать протокол по умолчанию для заданной комбинации домена и типа.
  • &$pair (обязательный): Переменная, передаваемая по ссылке, в которую будет записан массив из двух ресурсов сокетов при успешном выполнении.

Функция возвращает true в случае успеха и false в случае неудачи.

Простой пример с AF_UNIX

Создание пары сокетов в домене Unix для последующего использования в fork.

<?
$sockets = [];
if (socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets) === false) {
    echo "Не удалось создать пару сокетов: " . socket_strerror(socket_last_error());
} else {
    echo "Пара сокетов успешно создана.\n";
    echo "socket[0]: " . (is_resource($sockets[0]) ? 'Ресурс' : 'Не ресурс') . "\n";
    echo "socket[1]: " . (is_resource($sockets[1]) ? 'Ресурс' : 'Не ресурс') . "\n";
}
?>
Пара сокетов успешно создана.
socket[0]: Ресурс
socket[1]: Ресурс
Пример с флагом SOCK_DGRAM

Создание пары датаграммных сокетов.

<?
$sockets = [];
if (socket_create_pair(AF_INET, SOCK_DGRAM, 0, $sockets)) {
    $msg = "Тестовое сообщение";
    socket_write($sockets[0], $msg, strlen($msg));
    $data = socket_read($sockets[1], 1024);
    echo "Получено: $data\n";
}
?>
Получено: Тестовое сообщение
Альтернативы в PHP
  • stream_socket_pair: Создает пару подключенных, неименованных потоковых сокетов. Возвращает массив потоковых ресурсов. Часто считается более высокоуровневой и удобной альтернативой, особенно при работе с потоковыми контекстами.
  • pcntl_signal и разделяемая память: Для простых уведомлений между процессами иногда достаточно сигналов. Для сложного обмена данными можно использовать shmop или Semaphore.
  • popen/proc_open: Для взаимодействия с внешними процессами через стандартные потоки ввода-вывода.

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

Аналоги в других языках
Python (модуль os)
import os
# Создание пары сокетов
parent_conn, child_conn = os.pipe()  # Создает анонимный канал (pipe)
# Или с использованием socket.socketpair()
import socket
sock1, sock2 = socket.socketpair()
sock1.send(b'Hello')
print(sock2.recv(1024))  # b'Hello'
b'Hello'
JavaScript (Node.js)

В Node.js нет прямой аналогии для создания пар сокетов в одном процессе. Для IPC между процессами используются child_process.fork() и канал сообщений.

Socket create pair в MySQL

MySQL не предоставляет функций для низкоуровневой работы с сокетами в контексте IPC. Взаимодействие обычно происходит через сетевые соединения или встроенные функции для работы с данными.

Основное отличие PHP-функции - ее низкоуровневость и работа в контексте одного PHP-процесса с последующим fork, тогда как в Python socket.socketpair() возвращает полноценные объекты сокетов, а в Node.js акцент сделан на сетевые и межпроцессные коммуникации через события.

Типичные ошибки
Ошибка из-за недоступности домена
<?
$sockets = [];
// Попытка использовать несуществующий домен
if (!socket_create_pair(99999, SOCK_STREAM, 0, $sockets)) {
    echo "Ошибка: " . socket_strerror(socket_last_error()) . "\n";
}
?>
Ошибка: Invalid argument
Не передача массива по ссылке
<?
$sockets = [];
// Функция ожидает ссылку, но передается значение (в данном случае это вызовет предупреждение в строгом режиме)
// Корректно: socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);
if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, [$sockets])) {
    echo "Ошибка создания пары.\n";
}
?>
Warning: socket_create_pair() expects parameter 4 to be array, null given...
Попытка использовать сокеты после закрытия
<?
$sockets = [];
socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);
socket_close($sockets[0]);
if (socket_write($sockets[0], "test", 4) === false) {
    echo "Запись в закрытый сокет невозможна.\n";
}
?>
Запись в закрытый сокет невозможна.
Изменения в версиях PHP

Начиная с PHP 8.0, функция возвращает объекты Socket вместо ресурсов. Сокеты стали объектами.

В PHP 8.3 были улучшены сообщения об ошибках и общая стабильность работы с сокетами, но сигнатура функции socket_create_pair осталась неизменной.

<?
// Поведение в PHP 8.0+
$sockets = [];
socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);
var_dump($sockets[0]);
?>
object(Socket)#1 (0) {
}
Расширенные примеры
Взаимодействие родительского и дочернего процесса
Пример php
<?
$sockets = [];
if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) {
    die("Ошибка создания пары сокетов");
}

$pid = pcntl_fork();
if ($pid == -1) {
    die("Не удалось создать дочерний процесс");
}

if ($pid) { // Родительский процесс
    socket_close($sockets[0]); // Закрываем один конец в родителе
    $msg = "Сообщение от родителя";
    socket_write($sockets[1], $msg, strlen($msg));
    echo "Родитель отправил: $msg\n";
    
    $response = socket_read($sockets[1], 1024);
    echo "Родитель получил: $response\n";
    socket_close($sockets[1]);
    pcntl_wait($status);
} else { // Дочерний процесс
    socket_close($sockets[1]); // Закрываем другой конец в потомке
    $data = socket_read($sockets[0], 1024);
    echo "Дочерний процесс получил: $data\n";
    
    $reply = "Ответ от дочернего процесса";
    socket_write($sockets[0], $reply, strlen($reply));
    echo "Дочерний процесс отправил: $reply\n";
    socket_close($sockets[0]);
    exit(0);
}
?>
Родитель отправил: Сообщение от родителя
Дочерний процесс получил: Сообщение от родителя
Дочерний процесс отправил: Ответ от дочернего процесса
Родитель получил: Ответ от дочернего процесса
Использование с socket_select для асинхронного ожидания
Пример php
<?
$sockets = [];
socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);

// Записываем данные в один сокет
socket_write($sockets[0], "test data", 9);

$read = [$sockets[1]];
$write = $except = null;

// Ожидаем, пока сокет станет доступным для чтения
if (socket_select($read, $write, $except, 5) > 0) {
    foreach ($read as $readySocket) {
        $data = socket_read($readySocket, 1024);
        echo "Данные, готовые к чтению: $data\n";
    }
}
socket_close($sockets[0]);
socket_close($sockets[1]);
?>
Данные, готовые к чтению: test data
Передача файлового дескриптора (концептуально)

Прямая передача файловых дескрипторов через socket_create_pair невозможна. Однако, можно передать метаданные (например, имя файла или содержимое) через сокет, а затем открыть файл в другом процессе.

PHP socket_create_pair function comments

En
Socket create pair Creates a pair of indistinguishable sockets and stores them in an array