Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 120 · От основ к системному API

Sockets: собственный TCP/UDP протокол и полный API

От первого локального endpoint до всех функций, AddressInfo, Windows IPC, ancillary data и платформенных констант.

Начать с байтов, затем договориться о сообщении

Наглядная модель

Команда A42 проходит через два отдельных endpoint

Локальная лаборатория выполняет обе роли в одном процессе, но Socket клиента, listener и принятый peer остаются разными объектами.

  1. Listener: 127.0.0.1:0

    bind выбирает свободный порт, listen включает очередь. Listener принимает соединения и не читает A42.

  2. Клиент → выбранный порт

    connect устанавливает TCP-связь. accept возвращает отдельный серверный peer.

  3. A42: длина 3 байта

    socket_write передаёт байты клиенту ОС. Несколько чтений могут понадобиться даже после одной записи.

  4. Peer собирает A + 42

    Цикл дочитывает до согласованной длины. TCP сохраняет порядок байтов, но не границы вызовов write.

  5. Ответ accepted:A42:2

    Приложение проверяет команду и отправляет отдельное подтверждение. Сам успех write ещё не означает выполненный заказ.

Схема показывает роли и порядок действий, а не устройство пакетов в ОС. Loopback остаётся на этой машине; UDP требует отдельного договора о потере, повторе и порядке датаграмм.

Хотим запросить остаток без HTTP, передав собственную короткую команду product:1 . Сокет переносит байты, но не знает ни товаров, ни JSON, ни конца нашего сообщения. Поэтому участники договариваются: команда и ответ заканчиваются LF, ответ содержит JSON, размер каждой строки не больше 4096 байт. Для обычного веб-API удобнее cURL; прямые сокеты нужны, когда вы управляете протоколом.

Известный протокол каталога
  1. Клиент: product:1 + LF
  2. Обработчик: выбрать name=Молоток, stock=8
  3. Ответ: {"name":"Молоток","stock":8} + LF
  4. Клиент: JSON → поля → Молоток — 8 шт.

Расширение sockets должно присутствовать в php -m. Самый короткий шаг — передать уже известную строку через пару соединённых конечных точек. В данном опыте один PHP-процесс выполняет роли по очереди. Массив $pair получает два объекта Socket; индексы 0 и 1 выбирают разные стороны. Семейство на Windows — AF_INET, на Unix — AF_UNIX. Условия проверяют отказ; запись делается до чтения, чтобы обе стороны не ждали друг друга.

strlen считает байты, не русские буквы. .= дописывает полученный фрагмент к строке. while повторяет чтение, пока накоплено меньше заданного количества байтов; чтение потока не обещает выдать всё за один вызов. Короткую запись здесь проверяем целиком; следующая библиотека показывает и цикл записи.

Первая строка, полученная через сокетСамостоятельный пример
<?php
declare(strict_types=1);

$pair = [];
$family = PHP_OS_FAMILY === 'Windows' ? AF_INET : AF_UNIX;
if (!socket_create_pair($family, SOCK_STREAM, 0, $pair)) {
    throw new RuntimeException('Пара не создана');
}
$text = "Молоток — 8 шт.\n";
if (socket_write($pair[0], $text) !== strlen($text)) {
    throw new RuntimeException('Строка записана не целиком');
}
$received = '';
while (strlen($received) < strlen($text)) {
    $part = socket_read($pair[1], strlen($text) - strlen($received));
    if ($part === false || $part === '') { throw new RuntimeException('Строка оборвалась'); }
    $received .= $part;
}
echo $received;
socket_close($pair[0]);
socket_close($pair[1]);
Ожидаемый результат
Молоток — 8 шт.

Один самостоятельный example.php; выполнить php example.php с sockets. Выводится полученная строка, а не число записанных байтов. Размер известен заранее только в этом первом опыте; в протоколе ниже окончание определяется LF.

Полная подготовка: кто вызывает кого и где товар

Теперь строку должен выбрать сервер по команде. В следующих карточках повторяется require __DIR__ . '/local-net.php': это подключение нашего полного файла ниже. Имена помощников не встроены в PHP. Они вынесены, чтобы конкретную операцию было видно среди подготовки; архив никогда не требует найти неопубликованный код.

ПомощникВход, действие, выход
localSocket / localTimeoutСоздают Socket выбранного семейства/типа и задают сроки чтения/записи по 2 секунды.
localListenercreate → bind только 127.0.0.1 и порт 0 → listen. Порт 0 просит ОС выбрать свободный номер.
localTcpСлушатель, клиент и принятое соединение; возвращаются три разных объекта, поэтому закрываются отдельно.
writeBytesПолучает сокет и строку, дописывает остатки после частичной записи, при отказе прекращает опыт.
readCatalogLineНакапливает до LF, максимум 4096 байт. EOF до конца строки — оборванное сообщение.
serveProductЧитает product:1 или product:2, выбирает заданный товар, отправляет JSON с LF.
showProductДекодирует JSON, проверяет типы name/stock и выводит понятную строку.

Конструкция [$listener, $client, $peer] = localTcp() раскладывает три элемента возвращённого массива по переменным. Объявление function name(...): Socket обещает возвращаемый тип; return отдаёт результат вызвавшему коду. try / catch в подготовке позволяет освободить уже созданные ресурсы при отказе. Все каталожные данные находятся внутри serveProduct.

<?php
declare(strict_types=1);
function localSocket(int $family = AF_INET, int $type = SOCK_STREAM, int $protocol = SOL_TCP): Socket {
    $socket = socket_create($family, $type, $protocol);
    if ($socket === false) { throw new RuntimeException('Не удалось создать сокет'); }
    return $socket;
}
function localTimeout(Socket $socket): void {
    foreach ([SO_RCVTIMEO, SO_SNDTIMEO] as $option) {
        if (!socket_set_option($socket, SOL_SOCKET, $option, ['sec'=>2, 'usec'=>0])) {
            throw new RuntimeException('Не удалось установить таймаут');
        }
    }
}
function localListener(): Socket {
    $listener = localSocket();
    if (!socket_bind($listener, '127.0.0.1', 0) || !socket_listen($listener, 2)) {
        socket_close($listener);
        throw new RuntimeException('Локальный слушатель не создан');
    }
    return $listener;
}
function localTcp(): array {
    $listener = localListener();
    $client = localSocket();
    $peer = null;
    try {
        if (!socket_getsockname($listener, $address, $port)) { throw new RuntimeException('Адрес не прочитан'); }
        if (!socket_connect($client, $address, $port)) { throw new RuntimeException('Нет локального соединения'); }
        $peer = socket_accept($listener);
        if ($peer === false) { throw new RuntimeException('Соединение не принято'); }
        localTimeout($client);localTimeout($peer);
        return [$listener, $client, $peer];
    } catch (Throwable $error) {
        if ($peer instanceof Socket) { socket_close($peer); }
        socket_close($client);socket_close($listener);
        throw $error;
    }
}
function localPair(): array {
    $pair = [];
    $family = PHP_OS_FAMILY === 'Windows' ? AF_INET : AF_UNIX;
    if (!socket_create_pair($family, SOCK_STREAM, 0, $pair)) { throw new RuntimeException('Не удалось создать пару'); }
    foreach ($pair as $socket) { localTimeout($socket); }
    return $pair;
}
function writeBytes(Socket $socket, string $bytes): void {
    $offset = 0;
    while ($offset < strlen($bytes)) {
        $written = socket_write($socket, substr($bytes, $offset));
        if ($written === false || $written === 0) { throw new RuntimeException('Запись не продвигается'); }
        $offset += $written;
    }
}
function readCatalogLine(Socket $socket): string {
    $line = '';
    while (strlen($line) < 4096) {
        $byte = socket_read($socket, 1, PHP_BINARY_READ);
        if ($byte === false || $byte === '') { throw new RuntimeException('Ответ оборвался до конца строки'); }
        $line .= $byte;
        if ($byte === "\n") { return $line; }
    }
    throw new RuntimeException('Строка длиннее учебного предела');
}
function serveProduct(Socket $peer): void {
    $request = readCatalogLine($peer);
    $catalog = ["product:1\n"=>['name'=>'Молоток','stock'=>8], "product:2\n"=>['name'=>'Перчатки','stock'=>15]];
    $row = $catalog[$request] ?? ['error'=>'Неизвестная команда'];
    writeBytes($peer, json_encode($row, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)."\n");
}
function showProduct(string $json): void {
    $row = json_decode($json, true, 8, JSON_THROW_ON_ERROR);
    if (!is_array($row) || !is_string($row['name'] ?? null) || !is_int($row['stock'] ?? null)) {
        throw new RuntimeException('В ответе нет корректного товара');
    }
    echo $row['name'], ' — ', $row['stock'], " шт.\n";
}
Запросить товар по собственному протоколуСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

После подготовки клиент пишет product:1 с LF; сервер читает команду, выбирает Молоток и отправляет JSON; клиент читает до LF, проверяет поля и печатает остаток. Все функции определены в приложенном local-net.php.

Теперь запросим отсутствующий товар. Сетевой канал исправен, но прикладной ответ содержит error вместо name/stock. Клиент должен объяснить отказ человеку, а не объявить, что сокет сломан. isset выбирает ветку известной ошибки, finally закрывает три конечные точки при любом исходе чтения.

Объяснить отказ каталога, полученный через исправный каналСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
try {
    writeBytes($client, "product:99\n");
    serveProduct($peer);
    $json = readCatalogLine($client);
    $reply = json_decode($json, true, 8, JSON_THROW_ON_ERROR);
    if (isset($reply['error']) && is_string($reply['error'])) {
        echo 'Каталог не выдал товар: ', $reply['error'], "\n";
    } else {
        showProduct($json);
    }
} finally {
    socket_close($peer);
    socket_close($client);
    socket_close($listener);
}
Ожидаемый результат
Каталог не выдал товар: Неизвестная команда

Сервер получил product:99 и вернул JSON ошибки. Все байты переданы успешно; клиент прочитал реальный ответ и показал его смысл. Этот исход отличается от EOF, таймаута и would-block.

Упражнение: замените только команду на product:2 . Ответ должен стать Перчатки — 15 шт.. Неизвестная команда возвращает поле error; showProduct её не принимает за товар. Чтобы показывать такой отказ человеку, добавьте отдельную ветку проверки error до чтения name/stock. Эта лаборатория не создаёт публичный сервер и не моделирует параллельных посетителей.

Зачем нужен Socket: адрес, порт и две стороны обмена

Датчик отправляет показание T=21, а локальная программа резервирует товар A42. Обеим сторонам нужны правила: куда отправить байты, как отличить одно сообщение от другого, сколько ждать и что делать при разрыве. Расширение sockets даёт низкоуровневые средства связи ОС. Оно пригодится для собственного протокола и специальных сетевых опций; готовый HTTP клиент обычно проще строить через cURL, а обычные потоки/TLS — через Streams.

СловоНа примере локального склада
ПроцессОдна работающая программа PHP. В нашей лаборатории обе стороны можно создать внутри одного процесса.
Endpoint / SocketОдна конечная точка связи; ещё не сервер, не браузер и не строка с адресом.
Адрес127.0.0.1 означает эту машину. 0.0.0.0 при bind означает все IPv4 интерфейсы и расширяет доступ.
ПортЧисло 0…65535. bind с портом 0 просит ОС выбрать свободный; клиент затем получает фактическое число.
Listener и peerListener принимает новые TCP соединения; accept возвращает отдельный peer для обмена с конкретным клиентом.
TCPНадёжный упорядоченный поток байтов. Одна отправка может читаться частями, несколько отправок — вместе.
UDPОтдельные датаграммы. Возможны потеря, дублирование, изменение порядка; успешный send не доказывает получение.
BlockingВызов ждёт данные/свободный буфер; таймаут и общий срок задают осознанно.
AddressInfoОписание найденного адреса, из которого позднее можно создать Socket. Это ещё не соединение.
Что происходит с A42
  1. Клиент имеет свой Socket
  2. Сервер слушает только 127.0.0.1:свободный_порт
  3. accept создаёт отдельный серверный peer
  4. Клиент пишет байты A42
  5. Peer читает три байта; приложение понимает их по своему протоколу

Предпосылки: переменная и присваивание, условие и строгое сравнение, массив. Ниже сначала вызов функции разобран по частям; функции-обёртки, циклы и try/finally появляются после минимального создания. Значение Socket не показывает внутренние поля: PHP 8 заменил прежние resources непрозрачными объектами.

СинтаксисЧто он делает
$socket = socket_create(...)Вызвать фабрику и сохранить возвращённое значение в переменную. Запятая разделяет аргументы.
AF_INET, SOCK_STREAM, SOL_TCPИменованные целые константы: IPv4, поток, TCP. Строки с такими названиями не заменяют константы.
$socket === falseСтрого проверить отказ. Ноль байтов и пустая строка имеют другие значения.
$socket instanceof SocketУбедиться, что получен объект внутреннего класса Socket.
socket_close($socket)Освободить endpoint после работы; это отдельная операция, не отправка сообщения.

Этот вариант. Программа ещё ни к кому не подключается и не открывает порт. Положительный результат доказывает только создание endpoint. Дальше можно выбрать роль клиента (connect) либо listener (bind/listen).

Первый успех: создать и закрыть endpoint без сетиСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    echo "Endpoint не создан\n";
} else {
    echo "Готов объект Socket\n";
    socket_close($socket);
}
Ожидаемый результат
Готов объект Socket

Установка, capability и отличие от streams

Sockets — дополнительное расширение PHP, отличное от встроенной системы streams. Проверьте именно тот executable и ini, которыми запускается программа. php --ini и php -m относятся к CLI; FPM/Apache могут читать другой ini. У расширения sockets нет собственных runtime INI-директив. На Unix при сборке из исходников используется --enable-sockets; у пакетного PHP следует выбрать пакет своей версии/дистрибутива. Windows DLL должна совпадать с PHP по версии, архитектуре и TS/NTS.

Этот вариант. Ожидаемый вывод относится к процессу с загруженным sockets. Если получили «Нет расширения», не продолжайте к сетевым вызовам; сначала разберитесь с активным executable/ini. Эта проверка не гарантирует доступность raw sockets, IPv6 или Unix-only функций.

Проверить возможности текущего процессаСамостоятельный пример
<?php
declare(strict_types=1);

echo extension_loaded('sockets') ? "sockets загружено\n" : "Нет расширения\n";
echo class_exists('Socket', false) ? "Объект Socket доступен\n" : "Нет класса\n";
echo function_exists('socket_create') ? "Фабрика доступна\n" : "Нет функции\n";
Ожидаемый результат
sockets загружено
Объект Socket доступен
Фабрика доступна

Этот вариант. PowerShell, предусловия: php доступен через PATH; текущая папка содержит example.php; extension_dir указывает на совпадающий набор DLL; sockets ещё не загружено через ini. Команда не меняет общий ini. Если расширение уже загружено, дополнительный -d даст предупреждение — запустите просто php .\example.php.

Включить DLL только для одного учебного CLI процессаФрагмент или команда
php -d extension=sockets .\example.php

В этом проекте новые самостоятельные примеры проверяются PHP 8.5.11 Windows с process-only подключением подходящей DLL. Общий php.ini не меняется. Самостоятельные exchanges используют только собственные loopback endpoints и свободные порты; настройка сервиса, firewall или публичного listener не требуется. Примеры Unix/специальных ОС отмечены контекстными и здесь проходят только синтаксическую проверку. Официальный узел «Resource Types» исторический: сейчас открытый endpoint — Socket, resolver result — AddressInfo; streams при этом остаются resources.

Настройка окружения: установка PHP, активный php.ini. Точечную возможность проверяют defined для константы и function_exists для функции; затем всё равно проверяют результат ОС. Не подменяйте отсутствующую константу её числом с другой платформы.

Первый обмен и разбор каждой новой конструкции

Минимальный обмен удобно показать на паре соединённых endpoints. В Windows socket_create_pair использует AF_INET; на Unix выбираем AF_UNIX. Массив $pair сначала пустой, функция заполняет его по ссылке. Запись [$sender, $receiver] = $pair даёт два понятных имени элементам. Во втором шаге возвращается количество принятых байтов; при чтении ожидаем строку.

Этот вариант. Тернарное условие выбирает семейство по ОС. try/finally гарантирует уборку после успешного создания пары; throw прерывает ошибочный путь. while дочитывает до трёх байтов: даже короткое сообщение поток не обязан вернуть одним чтением. Для произвольной длинной записи аналогичный цикл нужен также отправителю.

Передать A42 через собственную паруСамостоятельный пример
<?php
declare(strict_types=1);

$pair = [];
$family = PHP_OS_FAMILY === 'Windows' ? AF_INET : AF_UNIX;
if (!socket_create_pair($family, SOCK_STREAM, 0, $pair)) throw new RuntimeException('Пара не создана');
[$sender, $receiver] = $pair;
try {
    if (!socket_set_option($receiver, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0])) throw new RuntimeException('Нет таймаута');
    if (socket_write($sender, 'A42') !== 3) throw new RuntimeException('Пример требует ровно три записанных байта');
    $message = '';
    while (strlen($message) < 3) {
        $part = socket_read($receiver, 3 - strlen($message));
        if ($part === false || $part === '') throw new RuntimeException('Неполное чтение');
        $message .= $part;
    }
    echo $message, "\n";
} finally { socket_close($sender); socket_close($receiver); }
Ожидаемый результат
A42

Три — размер именно этой ASCII строки. В UTF-8 русская буква обычно занимает несколько байтов: strlen измеряет сетевую длину, mb_strlen считает символы и для length протокола не подходит. Сокеты не меняют кодировку автоматически. Стороны заранее договариваются, например о UTF-8 для текста.

Локальная лаборатория: полный вспомогательный файл

Дальше примеры используют один приложенный файл local-sockets.php. Он не скрывает внешний сервер: сам создаёт пару, TCP listener/клиента/peer или два UDP endpoints на этой машине. Сохраните файл рядом с выбранным примером. require __DIR__ . "/local-sockets.php" загружает его по пути текущего файла; это не сетевой URL.

ПомощникЧто вернётся / кто закрывает
labSocketСозданный Socket; ошибки приводят к исключению вместо продолжения с false.
labPairДва связанных Socket с двухсекундными таймаутами чтения/записи.
labListenerTCP listener, привязанный только к 127.0.0.1 и свободному порту.
labTcpМассив [listener, client, peer]; закрыть все три.
labUdpМассив [receiver, sender, port]; оба endpoint привязаны к loopback, порт принадлежит receiver.
labWriteAll / labReadExactЦиклы обработки частичных операций; чтение ограничено 64 KiB. Не общий deadline всей операции.
labCloseЗакрыть переданные открытые Socket один раз; не передавать повторные ссылки одного объекта.

Этот вариант. Этот файл только определяет функции, поэтому сам ничего не печатает и не открывает endpoints. Типы Socket/int/string описывают аргументы; : array/void — результат. Оператор ... собирает переменное число аргументов labClose, foreach закрывает каждый. Это короткая учебная лаборатория, не сервер для production: ей не добавлены аутентификация, общий deadline и исчерпывающий cleanup каждого частично выполненного setup.

Полный файл local-sockets.phpСамостоятельный пример
<?php
declare(strict_types=1);

function labSocket(int $family, int $type, int $protocol): Socket {
    $socket = socket_create($family, $type, $protocol);
    if ($socket === false) throw new RuntimeException(socket_strerror(socket_last_error()));
    return $socket;
}

function labPair(): array {
    $pair = [];
    $family = PHP_OS_FAMILY === 'Windows' ? AF_INET : AF_UNIX;
    if (!socket_create_pair($family, SOCK_STREAM, 0, $pair)) {
        throw new RuntimeException(socket_strerror(socket_last_error()));
    }
    foreach ($pair as $socket) {
        socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);
        socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 2, 'usec' => 0]);
    }
    return $pair;
}

function labListener(): Socket {
    $listener = labSocket(AF_INET, SOCK_STREAM, SOL_TCP);
    if (!socket_bind($listener, '127.0.0.1', 0) || !socket_listen($listener, 2)) {
        $error = socket_last_error($listener);
        socket_close($listener);
        throw new RuntimeException(socket_strerror($error));
    }
    return $listener;
}

function labTcp(): array {
    $listener = labListener();
    if (!socket_getsockname($listener, $address, $port)) throw new RuntimeException('Нет адреса');
    $client = labSocket(AF_INET, SOCK_STREAM, SOL_TCP);
    if (!socket_connect($client, $address, $port)) throw new RuntimeException('Нет подключения');
    $peer = socket_accept($listener);
    if ($peer === false) throw new RuntimeException('Нет принятого клиента');
    foreach ([$client, $peer] as $socket) {
        socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);
        socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 2, 'usec' => 0]);
    }
    return [$listener, $client, $peer];
}

function labUdp(): array {
    $receiver = labSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
    if (!socket_bind($receiver, '127.0.0.1', 0)) throw new RuntimeException('Нет привязки UDP');
    if (!socket_getsockname($receiver, $address, $port)) throw new RuntimeException('Нет адреса UDP');
    socket_set_option($receiver, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);
    $sender = labSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
    if (!socket_bind($sender, '127.0.0.1', 0)) throw new RuntimeException('Нет адреса отправителя');
    socket_set_option($sender, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);
    return [$receiver, $sender, $port];
}

function labWriteAll(Socket $socket, string $data): void {
    $offset = 0;
    while ($offset < strlen($data)) {
        $written = socket_write($socket, substr($data, $offset));
        if ($written === false || $written === 0) throw new RuntimeException('Запись не продвигается');
        $offset += $written;
    }
}

function labReadExact(Socket $socket, int $length): string {
    if ($length < 0 || $length > 65536) throw new InvalidArgumentException('Учебный предел 64 KiB');
    $data = '';
    while (strlen($data) < $length) {
        $chunk = socket_read($socket, $length - strlen($data), PHP_BINARY_READ);
        if ($chunk === false || $chunk === '') throw new RuntimeException('Неполный ответ');
        $data .= $chunk;
    }
    return $data;
}

function labClose(Socket ...$sockets): void {
    foreach ($sockets as $socket) socket_close($socket);
}
Ожидаемый результат

TCP: listener, принятое соединение и ответ

TCP слушатель выполняет create → bind → listen. Клиент делает create → connect. Затем accept создаёт отдельную серверную сторону уже установленного соединения. Ошибочно писать данные через listener: байты конкретного клиента идут через peer. В многопроцессной системе эти действия распределены по программам; здесь клиент подключается перед accept в том же процессе, поэтому не нужен фоновый сервер.

Этот вариант. Успешная запись команды означает только передачу байтов стеку; ответ accepted — отдельное прикладное подтверждение. Пример не хранит склад и не гарантирует идемпотентность повторной команды. При разрыве после отправки приложение не всегда знает, выполнил ли сервер действие; используйте request ID/журнал обработки. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Локальная команда склада с явным ответомСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$listener, $client, $peer] = labTcp();
try {
    labWriteAll($client, "reserve:A42:2\n");
    $request = socket_read($peer, 128, PHP_NORMAL_READ);
    if ($request === false || $request === '') throw new RuntimeException('Нет запроса');
    if ($request !== "reserve:A42:2\n") throw new RuntimeException('Команда вне учебного договора');
    labWriteAll($peer, "accepted:A42:2\n");
    echo labReadExact($client, strlen("accepted:A42:2\n"));
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
accepted:A42:2

Ограничение по разделителю работает лишь при оговорённом алфавите/экранировании. Если поле само может содержать перевод строки, нужен escaping либо длина перед payload. Нельзя читать до закрытия для сервиса, который держит соединение открытым: обе стороны могут ждать друг друга.

Поток не равен сообщению: длина, JSON и ограничения

Длина и тело — два разных элемента протокола. В следующем кадре первые четыре байта — беззнаковое целое в network byte order, затем ровно столько байтов UTF-8 JSON. pack("N", $length) кодирует заголовок; unpack("Nlength", $header) читает число под ключом length. Прежде чем выделять память/читать тело, проверяем собственный предел 1024 байта.

Один JSON кадр
  1. JSON содержит sku=A42 и qty=2
  2. strlen считает байты JSON
  3. pack N делает заголовок длины
  4. Получатель дочитывает 4 байта
  5. Проверяет предел и дочитывает тело
  6. json_decode проверяет формат, приложение — поля

Этот вариант. Проверки различают транспорт, структуру JSON и смысл команды. Внутри кадра нельзя использовать unserialize для недоверенных объектов. Даже этот ограниченный пример не добавляет TLS/аутентификацию и не является публичным endpoint. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Передать ограниченный JSON кадр без путаницы TCP границСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    $json = json_encode(['sku' => 'A42', 'qty' => 2], JSON_THROW_ON_ERROR);
    labWriteAll($sender, pack('N', strlen($json)) . $json);
    $header = unpack('Nlength', labReadExact($receiver, 4));
    $length = $header['length'];
    if ($length < 1 || $length > 1024) throw new RuntimeException('Кадр слишком велик/пуст');
    $data = json_decode(labReadExact($receiver, $length), true, 8, JSON_THROW_ON_ERROR);
    if (!is_array($data) || ($data['sku'] ?? null) !== 'A42' || ($data['qty'] ?? null) !== 2) throw new RuntimeException('Неверные поля');
    echo $data['sku'], ': ', $data['qty'], "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
A42: 2

Продолжение: JSON и его ошибки, pack/unpack и байтовые строки. Декодируйте только полный кадр; при EOF посередине кадра сохранённый префикс не превращается в допустимый запрос. Максимальный кадр, суммарный буфер, число клиентов и общий срок — разные лимиты.

UDP: отдельное показание и ответ его отправителю

UDP удобно для небольшого показания, потерю которого заменит следующее измерение. Программа приёма сначала bind; отправитель sendto указывает назначение каждого пакета. recvfrom возвращает фактический адрес/порт отправителя: именно туда можно послать ответ. Это не проверка доверия. Протокол с побочными действиями требует аутентификации, защиты от повторов и ограничений ответа, иначе возникает отражённый трафик.

Этот вариант. Номер 7 связывает показание и ответ. Здесь проверяется единственный локальный пакет; настоящий датчик должен учитывать потерю ACK, дубликаты, порядок и предел повторов. Пустой UDP пакет допустим и не означает закрытие peer. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Принять показание и подтвердить его номерСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    $reading = 'id=7;T=21';
    if (socket_sendto($sender, $reading, strlen($reading), 0, '127.0.0.1', $port) !== strlen($reading)) throw new RuntimeException('Не отправлено');
    $count = socket_recvfrom($receiver, $data, 64, 0, $from, $fromPort);
    if ($count === false || $data !== $reading) throw new RuntimeException('Нет допустимого показания');
    if (socket_sendto($receiver, 'ack=7', 5, 0, $from, $fromPort) !== 5) throw new RuntimeException('Нет ACK');
    if (socket_recvfrom($sender, $reply, 64, 0, $host, $replyPort) === false) throw new RuntimeException('Нет ответа');
    echo $data, "\n", $reply, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
id=7;T=21
ack=7

Ожидание, готовность и общий deadline

Non-blocking не делает CPU работу параллельной: он меняет поведение ожидания. Если данных нет, API возвращает false с EWOULDBLOCK/EAGAIN; программа использует select, чтобы ждать события. select меняет переданные массивы, поэтому на новой итерации формируют их снова. Готовность к чтению включает EOF; готовность к записи не означает доставку peer. Нельзя постоянно включать исправный Socket в write без накопленных данных: select будет немедленно возвращать его.

Этот вариант. hrtime даёт монотонное время; остаток срока уменьшается на каждом круге, а не начинается заново. Готовность может измениться до чтения, поэтому would-block всё равно проверяется. Длина и число участников здесь малы; это учебный цикл, не полноценный event loop. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Дочитать три байта с одним общим сроком ожиданияСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    labWriteAll($sender, 'A42');
    if (!socket_set_nonblock($receiver)) throw new RuntimeException('Нет non-blocking');
    $deadline = hrtime(true) + 2_000_000_000;
    $data = '';
    while (strlen($data) < 3) {
        $left = $deadline - hrtime(true);
        if ($left <= 0) throw new RuntimeException('Общий срок истёк');
        $seconds = intdiv($left, 1_000_000_000);
        $micros = intdiv($left % 1_000_000_000, 1000);
        $read = [$receiver]; $write = []; $except = [];
        $ready = socket_select($read, $write, $except, $seconds, $micros);
        if ($ready === false) throw new RuntimeException('Ошибка select');
        if ($ready === 0) continue;
        $chunk = @socket_read($receiver, 3 - strlen($data));
        if ($chunk === false) {
            $error = socket_last_error($receiver);
            if ($error === SOCKET_EWOULDBLOCK || (defined('SOCKET_EAGAIN') && $error === SOCKET_EAGAIN)) continue;
            throw new RuntimeException(socket_strerror($error));
        }
        if ($chunk === '') throw new RuntimeException('EOF раньше полного сообщения');
        $data .= $chunk;
    }
    echo $data, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
A42

Для non-blocking connect допустимы EINPROGRESS/EALREADY/EWOULDBLOCK по платформе: дождитесь write/except с оставшимся сроком, затем прочитайте SO_ERROR. Нулевой SO_ERROR подтверждает завершение TCP подключения, но ещё не подтверждает прикладной протокол. select имеет системные ограничения числа descriptors (особенно Windows FD_SETSIZE); для большой нагрузки нужен поддержанный event loop/другая архитектура. При EINTR можно повторить ожидание только с пересчитанным общим сроком.

Ошибки, безопасность протокола и уборка ресурсов

Системная ошибка — число и поясняющая строка; warning PHP может содержать английский префикс и локализованное сообщение ОС. В логике сравнивайте код с доступной константой, а не текст socket_strerror. У каждой операции различайте false, 0 и пустую строку. Закрытый объект вызывает Error; неверный тип — TypeError, неверный диапазон современных API может дать ValueError. Сам факт Error не означает сетевой сбой.

СлучайРеакция
Создание вернуло falseSocket ещё нет: socket_last_error() без аргумента.
Операция endpoint вернула falseСразу сохранить socket_last_error($socket), затем лог/cleanup. Для отдельных advanced путей смотрите договор глобальной ошибки.
Would-blockОжидать готовность, соблюдать deadline; не печатать ложный «сервер умер».
EOF TCPЗавершить чтение; если кадр неполный, отказать ему.
Ноль UDPДопустимая пустая датаграмма, не EOF.
Timeout после отправки командыНеизвестно, выполнил ли peer действие: нужен idempotency/request ID.
Очистка ошибкиsocket_clear_error меняет PHP сохранённый код; SO_ERROR — отдельный код ядра.

Собственный сервер не должен выполнять команду оболочки, eval или произвольный файловый путь из полученных байтов. Сначала ограничьте размер, разберите формат и разрешённые значения; добавьте доверие/аутентификацию и шифрование по протоколу. Для TLS воспользуйтесь подходящим stream/библиотекой, а не отправляйте пароль открытым sockets TCP. Закрывайте endpoints в finally; не делайте повторный close двух переменных, указывающих на один объект.

Ancillary, multicast и передача endpoint между процессами

Ancillary data — метаданные системного сообщения, не ещё одно поле JSON. recvmsg принимает размеры обычного и control буферов; cmsg_space рассчитывает второй с учётом ABI. На Unix SCM_RIGHTS позволяет доверенному процессу передать уже открытый descriptor, не сообщая имя файла. На Windows для общего socket используют WSA export/import с конкретным PID — это иной механизм.

Этот вариант. Требования: Unix build с SCM_RIGHTS, локальный tmpfile, только собственная пара. Здесь выполнена синтаксическая проверка на Windows, Unix runtime не заявлен. Дескрипторы могут разделять offset файла; это не независимая копия его содержимого. В настоящем IPC проверьте peer, тип/число всех присланных ресурсов и закройте лишние.

Unix-only: передать открытый временный файл через SCM_RIGHTSПример для указанного окружения
<?php
declare(strict_types=1);

if (PHP_OS_FAMILY === 'Windows' || !defined('SCM_RIGHTS')) throw new RuntimeException('Нужны Unix sockets и SCM_RIGHTS');
$pair = [];
if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $pair)) throw new RuntimeException('Нет пары');
[$sender, $receiver] = $pair;
$file = tmpfile(); if ($file === false) throw new RuntimeException('Нет временного файла');
$received = null;
try {
    fwrite($file, "A42:7\n"); rewind($file);
    socket_set_option($receiver, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);
    $sent = socket_sendmsg($sender, ['iov' => ['F'], 'control' => [
        ['level' => SOL_SOCKET, 'type' => SCM_RIGHTS, 'data' => [$file]],
    ]], 0);
    if ($sent !== 1) throw new RuntimeException('Нет передачи');
    $message = ['buffer_size' => 1, 'controllen' => socket_cmsg_space(SOL_SOCKET, SCM_RIGHTS, 1)];
    if (socket_recvmsg($receiver, $message, 0) !== 1 || ($message['flags'] & MSG_CTRUNC)) throw new RuntimeException('Неполный control');
    $received = $message['control'][0]['data'][0];
    if (!is_resource($received)) throw new RuntimeException('Не файловый stream');
    echo fgets($received);
} finally {
    if (is_resource($received)) fclose($received);
    fclose($file); socket_close($sender); socket_close($receiver);
}
Ожидаемый результат
A42:7

Multicast требует отдельной настройки интерфейса и членства MCAST_JOIN_GROUP, затем корректного leave/close. Оно не гарантирует доставку и не заменяет авторизацию. IPv4 и IPv6 используют разные уровни. Windows multicast loopback действует на принимающей стороне, BSD-подобные стеки — на отправляющей; актуальная документация Winsock уточняет это даже если старый текст PHP.net меняет стороны местами. Наши самостоятельные примеры не отправляют broadcast/multicast наружу.

Как читать полный каталог констант

Все 298 именованных строк актуальной страницы PHP.net сохранены ниже, включая четыре ошибочных написания с явным исправлением. Имена — целые константы, но роль различается: семейство, тип, бит flags, уровень, имя опции, её значение либо код ошибки. Числа зависят от ОС и не являются переносимым API. Константа появляется только при загруженном расширении и поддержанных заголовках/сборке; defined("ИМЯ") проверяет наличие, а не работоспособность сочетания.

Пометка версии означает появление в PHP, а не поддержку любой Windows/Linux версии. Отсутствующая пометка не обещает существование во всех старых PHP. У SO_*/TCP_*/IP_* defaults задаются ОС; конкретный endpoint проверяют get_option там, где чтение поддержано. Для сложных native структур наличие имени не гарантирует PHP конвертер: BPF/labels/splice/meminfo требуют отдельной системной интеграции. Для обычного UDP/TCP достаточно простых структур из карточек set/get_option.

Семейства, типы, уровни и режимы

Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.

Константа (int)Роль, значение и ограничение
AF_UNIXСемейство локального IPC: адресом служит путь/локальное имя. Поддержка Windows Unix sockets не означает переносимость socket_create_pair с этим семейством.
AF_INETСемейство IPv4; адрес и порт передаются раздельно, например 127.0.0.1 и 8080.
AF_INET6Семейство IPv6 при поддержке сборки/ОС; адрес ::1 без URL-скобок. Доступность IPv6 не гарантирует маршрут.
AF_DIVERTFreeBSD divert: получение пакетов, перенаправленных firewall. Низкоуровневый привилегированный интерфейс, не обычный TCP listener; наличие константы не обещает поддержку всеми обёртками PHP. С PHP 8.3.0.
AF_PACKETLinux уровень канала (OSI 2), сырые кадры сетевого устройства. Обычно нужны CAP_NET_RAW/права; PHP 8.5 понимает это семейство при создании и чтении собственного адреса. С PHP 8.5.0.
SOCK_STREAMУпорядоченный поток байтов, обычно TCP. Границы отдельных write не сохраняются; приложение задаёт framing.
SOCK_DGRAMДатаграммы, обычно UDP: границы сохраняются, доставка/порядок/отсутствие дубликатов не гарантируются.
SOCK_RAWДоступ к низкоуровневому протоколу/пакетам; требуется поддержка ОС и обычно особые права. Для HTTP не нужен.
SOCK_SEQPACKETСоединение с надёжными упорядоченными сообщениями и сохранением границ; поддерживается не каждым семейством/ОС.
SOCK_RDMНадёжные датаграммы без обещания порядка; часто не реализован. Проверяйте отказ socket_create, не подменяйте молча STREAM.
SOCK_CONN_DGRAMNetBSD connection-oriented datagram type; тип требует поддержанного протокола, не аналог универсального UDP connect. С PHP 8.4.0.
SOCK_DCCPDatagram Congestion Control Protocol: контроль перегрузки без TCP надёжности. PHP.net указывает NetBSD; фактическая константа условна по заголовкам ОС. С PHP 8.4.0.
SOCK_NONBLOCKБит к type при create, если доступен: сразу создать неблокирующий endpoint. Не аргумент flags для чтения. С PHP 8.4.0.
SOCK_CLOEXECБит type: закрывать descriptor при exec нового образа процесса. Не означает закрытие при обычном вызове PHP-функции. С PHP 8.4.0.
SOL_FILTERIllumos/Solaris уровень socket filters для FIL_ATTACH/FIL_DETACH. Не SOL_SOCKET и не Linux BPF уровень. С PHP 8.2.0.
SOL_UDPLITEУровень UDP-Lite для UDPLITE_* checksum coverage; UDP-Lite — отдельный протокол, не обычный UDP. С PHP 8.3.0.
SOL_SOCKETУровень общих SO_* свойств в get/set_option; не номер TCP протокола.
PHP_NORMAL_READТретий аргумент socket_read: читать до CR или LF, включая разделитель. Это режим PHP, не бит MSG_*.
PHP_BINARY_READТретий аргумент socket_read: бинарное чтение до указанного максимума, без трактовки CR/LF. Значение по умолчанию.
SOL_TCPУровень TCP_* настроек и номер TCP при create; применим к TCP endpoint.
SOL_UDPНомер UDP/уровень его опций; обычные SO_* всё равно используют SOL_SOCKET.
IPPROTO_IPУровень IPv4 IP_* опций, например multicast; не идентификатор семейства AF_INET.
IPPROTO_IPV6Уровень IPv6 IPV6_* опций и ancillary data; не AF_INET6.
SOL_LOCALBSD уровень LOCAL_* параметров локальных sockets; отличается от SOL_SOCKET.
SOMAXCONNСистемная граница/default запроса backlog; ядро может ограничить дополнительно. С PHP 8.4 default create_listen, но не default socket_listen (0).
SHUT_RDsocket_shutdown mode: прекратить дальнейший приём; не освобождает объект. PHP 8.5, числовое значение соответствует 0. С PHP 8.5.0.
SHUT_WRsocket_shutdown mode: завершить отправку, сохранив возможность чтения; PHP 8.5, соответствует 1. С PHP 8.5.0.
SHUT_RDWRsocket_shutdown mode: обе стороны; PHP 8.5, соответствует 2. Затем endpoint закрывают отдельно. С PHP 8.5.0.
IPPROTO_ICMPНомер ICMP при создании поддержанного IPv4 raw socket. Нужны соответствующие права; не порт UDP. С PHP 8.5.0.
IPPROTO_ICMPV6Номер ICMPv6 для поддержанного IPv6 raw socket; отдельные правила checksum/прав ОС. С PHP 8.5.0.

Флаги resolver AI_*

Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.

Константа (int)Роль, значение и ограничение
AI_ADDRCONFIGResolver flags: выбирать семейства по конфигурации адресов машины; loopback может не считаться достаточной настройкой ОС.
AI_ALLВместе с AI_V4MAPPED запрашивать и IPv6, и IPv4-mapped варианты согласно resolver ОС.
AI_CANONIDNResolver flags: преобразовывать canonical name из IDN-представления на поддержанной системе; зависит от locale/resolver.
AI_CANONNAMEЗапросить canonical name в первом результате, если resolver его предоставляет. Это не доказательство доверия узлу.
AI_IDNРазрешить resolver преобразовать международное имя по правилам locale/IDN поддержанной системы; не переносимый универсальный UTF-8 валидатор.
AI_NUMERICHOSTЗапретить DNS/поиск имён для host: требуется числовой IP адрес.
AI_NUMERICSERVservice должен быть числовой строкой порта, без поиска имени службы.
AI_PASSIVEResolver flag для адресов сервера/bind; с отсутствующим host native API выбирает wildcard. В PHP host обязателен строкой, поэтому явно выбирайте свой локальный адрес.
AI_V4MAPPEDДля IPv6 resolver разрешить IPv4-mapped результаты при отсутствии подходящих IPv6; сочетание с AI_ALL меняет выбор.

Флаги сообщений MSG_*

Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.

Константа (int)Роль, значение и ограничение
MSG_OOBФлаг отправки/чтения urgent out-of-band данных поддержанного протокола. TCP urgent data имеет особые правила и не является очередью произвольных сообщений.
MSG_WAITALLПросить чтение ждать запрошенные байты. EOF, сигнал/ошибка и ряд режимов могут вернуть меньше: длину всё равно проверяют.
MSG_PEEKПрочитать доступные данные, не удаляя их из входной очереди; следующий обычный recv увидит их снова.
MSG_DONTROUTEОтправка только непосредственно доступным интерфейсом без обычной маршрутизации; поддержка/эффект зависят от протокола и ОС.
MSG_EORОтметить конец записи в протоколе с границами записей. На Windows не предоставляется; TCP framing за вас не создаёт.
MSG_EOFПлатформенный флаг завершения отправки (например BSD). Не переносимый признак возврата EOF; Windows не предоставляет.
MSG_ZEROCOPYLinux запрос zero-copy отправки после SO_ZEROCOPY. Требует контроля completion через error queue и времени жизни памяти; наличие PHP-константы не доказывает безопасный zero-copy цикл PHP-строк. С PHP 8.2.0.
MSG_CMSG_CLOEXECLinux recvmsg flags: close-on-exec для полученных SCM_RIGHTS descriptors. Не закрывает их после обычного PHP вызова.
MSG_CONFIRMLinux send flags: подтвердить достижимость соседа для neighbour discovery; не прикладной ACK.
MSG_CTRUNCВыходной recvmsg flag: ancillary control обрезан; данных/дескрипторов может не хватать для безопасной обработки.
MSG_DONTWAITНеблокирующий режим конкретного send/recv вызова, если поддержан. Отличается от постоянного set_nonblock.
MSG_ERRQUEUELinux recvmsg flags: читать асинхронную error queue. PHP не декодирует любую native control-структуру этой очереди автоматически.
MSG_MORELinux send flags: есть ещё данные для объединения; эффект TCP/UDP различается. Не заменяет framing и может задержать отправку.
MSG_NOSIGNALLinux/поддержанные Unix send flags: не посылать SIGPIPE при отказе записи; ошибка EPIPE всё равно проверяется.
MSG_TRUNCВыходной recvmsg flag усечения datagram; как входной флаг в некоторых ОС меняет возвращаемую длину. Возвращённое число тогда может превышать реально скопированные байты.
MSG_WAITFORONELinux recvmmsg flag: после первого сообщения не блокировать последующие. В PHP sockets нет обёртки recvmmsg; наличие имени не добавляет её к recvmsg.

Общие SO_* и TCP настройки

Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.

Константа (int)Роль, значение и ограничение
SO_DEBUGSOL_SOCKET, int 0/1: системная диагностика сокета; зависит от прав/реализации. Microsoft providers обычно не выводят диагностику.
SO_REUSEADDRSOL_SOCKET, int 0/1 до bind: разрешить определённые случаи повторного использования адреса. Семантика Unix/Windows различается; не универсальная безопасная настройка любого сервера.
SO_REUSEPORTSOL_SOCKET, int 0/1 до bind: разрешить совместное использование порта по правилам ОС. Linux/macOS/BSD; Windows не предоставляет. Балансировка и требования к UID различаются.
SO_KEEPALIVESOL_SOCKET, int 0/1: включить TCP keepalive-пробы. Интервалы задаёт ОС/специальные TCP_* опции; не заменяет прикладной ping/deadline.
SO_DONTROUTESOL_SOCKET, int 0/1: ограничить маршрутизацию исходящих пакетов. Реальный эффект зависит от провайдера; это не ACL.
SO_LINGERSOL_SOCKET, массив l_onoff/l_linger: политика ожидания данных при close. Включённое значение с нулевым сроком способно вызвать TCP reset; не применять без понимания потери данных.
SO_BROADCASTSOL_SOCKET, int 0/1: разрешение отправки IPv4 broadcast datagrams. Не делает unicast адрес broadcast и не включает multicast.
SO_OOBINLINESOL_SOCKET, int 0/1: доставлять urgent данные в обычном входном потоке; меняет способ чтения MSG_OOB/atmark.
SO_SNDBUFSOL_SOCKET, int байтов: размер буфера отправки. ОС ограничивает/корректирует; проверяйте get_option, не предполагайте точное равенство.
SO_RCVBUFSOL_SOCKET, int байтов: буфер приёма. Не максимальный размер вашего PHP сообщения и не автоматическое ограничение длины команды.
SO_SNDLOWATSOL_SOCKET, int байтов: low-water отметка отправки; поддержка изменения ограничена. Windows TCP/IP не поддерживает эту опцию.
SO_RCVLOWATSOL_SOCKET, int байтов: low-water отметка приёма/готовности по правилам ОС. Windows TCP/IP не поддерживает.
SO_SNDTIMEOSOL_SOCKET, массив sec/usec: предел блокирующей отправки. Windows округляет до миллисекунд; не общий срок всех записей.
SO_RCVTIMEOSOL_SOCKET, массив sec/usec: предел блокирующего чтения; ноль обычно снимает его. Не таймаут DNS/всей сессии.
SO_TYPESOL_SOCKET, чтение int SOCK_*: тип уже созданного endpoint. Не переключается записью.
SO_ERRORSOL_SOCKET, чтение int: отложенная ошибка ядра с очисткой по системному договору. Проверяют после non-blocking connect; отличается от сохранённого socket_last_error PHP.
SO_ZEROCOPYLinux SOL_SOCKET, int 0/1: разрешить zero-copy механизм. Требует полноценной поддержки completion/буферов, а не только set_option true. С PHP 8.2.0.
TCP_NODELAYSOL_TCP, int 0/1: отключить Nagle для мелких записей. Не гарантирует одно write = один пакет/ответ.
TCP_KEEPCNTSOL_TCP, int: число неотвеченных keepalive проб перед признанием соединения неисправным; поддержка/границы ОС. С PHP 8.2.0.
TCP_KEEPIDLESOL_TCP, int секунд: простой до первой keepalive пробы; нужен SO_KEEPALIVE. С PHP 8.2.0.
TCP_KEEPINTVLSOL_TCP, int секунд: интервал между keepalive пробами; не частота сообщений приложения. С PHP 8.2.0.
TCP_KEEPALIVEПлатформенное SOL_TCP имя времени простоя keepalive (в частности macOS), int секунд. Не путать с общим флагом SO_KEEPALIVE. С PHP 8.2.0.
TCP_NOTSENT_LOWATLinux SOL_TCP, int байтов: порог ещё не переданных TCP байтов для готовности/буферизации. Не лимит всего ответа. С PHP 8.2.0.
SO_MARKLinux SOL_SOCKET, int: mark пакетов для политики маршрутизации/фильтрации; нужны соответствующие capabilities. С PHP 8.1.0.
SO_USER_COOKIEFreeBSD SOL_SOCKET, int: пользовательская метка пакетов endpoint для системных механизмов. Не HTTP cookie. С PHP 8.1.0.
SO_RTABLEOpenBSD SOL_SOCKET, int: таблица маршрутизации endpoint; допускаемые значения/права задаёт ОС. С PHP 8.2.0.
SO_ACCEPTFILTERFreeBSD/NetBSD SOL_SOCKET: строка имени установленного accept filter. Задерживает готовность accept до условия ядра; не PHP callback. Имя ограничено системным полем (обычно 15 байтов). С PHP 8.1.0.
SO_DONTTRUNCmacOS SOL_SOCKET, int 0/1: изменить поведение обработки длинных сообщений/усечения. Согласуйте с буфером и системным договором; не переносимо. С PHP 8.1.0.
SO_WANTMOREmacOS SOL_SOCKET, int 0/1: расширенное уведомление о наличии данных сверх текущего запроса. Не переносимый способ узнать длину TCP сообщения. С PHP 8.1.0.
SO_INCOMING_CPULinux SOL_SOCKET, int: CPU, связанный с входящим потоком; тюнинг привязки обработки, не номер PHP worker. С PHP 8.2.0.
SO_MEMINFOLinux SOL_SOCKET: native массив счётчиков памяти endpoint. Наличие имени не означает полноценного array-конвертера этой структуры в PHP get_option; не считайте общий int полным отчётом. С PHP 8.2.0.
SO_BPF_EXTENSIONSLinux SOL_SOCKET, чтение int: уровень поддержанных classic BPF расширений. Это capability ядра, не загрузка программы фильтра. С PHP 8.2.0.
SO_SETFIBFreeBSD SOL_SOCKET, int: номер FIB/таблицы маршрутизации. Нужен заранее настроенный системный routing context. С PHP 8.2.0.
SO_ATTACH_REUSEPORT_CBPFLinux SOL_SOCKET: native sock_fprog выбирает endpoint в reuseport-группе. PHP не предоставляет произвольный конструктор этой C-структуры через обычный int; наличие константы не делает пример C готовым PHP-кодом. С PHP 8.3.0.
SO_DETACH_BPFLinux SOL_SOCKET: снять ранее прикреплённый фильтр; синоним системного SO_DETACH_FILTER. Операция может требовать права. С PHP 8.3.0.
SO_DETACH_FILTERLinux SOL_SOCKET: удалить фильтр endpoint; не путать с закрытием самого Socket. С PHP 8.3.0.
SO_RERRORNetBSD SOL_SOCKET, int 0/1: уведомлять о переполнении входного буфера. Не общий переключатель PHP исключений. С PHP 8.3.0.
SO_ZEROIZEOpenBSD SOL_SOCKET, int 0/1: обнулять освобождаемые socket buffers. Не гарантирует очистку всех копий секретной PHP-строки. С PHP 8.3.0.
SO_SPLICEOpenBSD SOL_SOCKET: native структура соединения двух endpoints в ядре. PHP универсальный set_option не строит такую структуру автоматически; не равнозначно stream_copy_to_stream. С PHP 8.3.0.
SO_REUSEPORT_LBFreeBSD SOL_SOCKET, int 0/1: reuse-port с распределением нагрузки. Настраивается согласованно до bind, не переносимо в Windows. С PHP 8.3.0.
TCP_DEFER_ACCEPTLinux SOL_TCP, int секунд: пробуждать listener при поступлении данных, с системными ограничениями. Не заменяет обработку таймаута клиента. С PHP 8.1.0.
TCP_CONGESTIONLinux SOL_TCP: строка имени доступного алгоритма congestion control при set. PHP getter общего int не следует принимать за полноценный строковый getsockopt этого native параметра. С PHP 8.2.0.
TCP_QUICKACKLinux SOL_TCP, int 0/1: временно изменить delayed ACK поведение. Стек может затем сменить режим; не постоянная гарантия задержки. С PHP 8.3.0.
TCP_REPAIRLinux SOL_TCP, int: режим восстановления TCP состояния, предназначенный для специальных системных инструментов и прав, не обычного приложения. С PHP 8.3.0.
TCP_SYNCNTLinux SOL_TCP, int: число повторных SYN до прекращения подключения; не точное число секунд connect timeout. С PHP 8.4.0.
SO_BINDTODEVICELinux SOL_SOCKET, строка: ограничить endpoint сетевым устройством, например lo; требуются права, не переносимо в Windows.
SO_FAMILYПлатформенное SOL_SOCKET чтение семейства endpoint. Не менять семейство существующего Socket записью.
SO_LABELBSD/macOS security label endpoint: native сложная структура/политика MAC. PHP generic int не является полноценным преобразованием label.
SO_LISTENQLENFreeBSD SOL_SOCKET чтение int: текущая длина очереди listener, не количество всех клиентов приложения.
SO_LISTENQLIMITFreeBSD SOL_SOCKET чтение int: установленный предел очереди listener.
SO_PASSCREDLinux SOL_SOCKET, int 0/1: включить доставку credentials ancillary у Unix sockets; используйте recvmsg и проверку доверия peer.
SO_PEERLABELBSD/macOS security label peer: native MAC структура; наличие константы не означает автоматическую декодировку label в PHP.
SO_EXCLUSIVEADDRUSEWindows SOL_SOCKET, int 0/1 до bind: запретить принудительное повторное связывание адреса/порта другим socket. С PHP 8.4.0.
SO_EXCLBINDSolaris SOL_SOCKET, int 0/1: эксклюзивное связывание адреса по системным правилам. С PHP 8.4.0.
SO_NOSIGPIPEmacOS/FreeBSD SOL_SOCKET, int 0/1: подавить SIGPIPE при записи; ошибка операции остаётся, её надо обработать. С PHP 8.4.0.
SO_LINGER_SECmacOS SOL_SOCKET: вариант linger с единицей секунды вместо тиков. PHP-конвертер зависит от option: не переносите автоматически массив SO_LINGER без проверки поддержки конкретной сборки. С PHP 8.4.0.
SO_BINDTOIFINDEXSOL_SOCKET, int: индекс интерфейса вместо его имени; права и поддержка ОС обязательны. С PHP 8.4.0.
SO_BUSY_POLLLinux SOL_SOCKET, int микросекунд: короткое busy polling при блокирующем приёме. Уменьшение задержки оплачивается CPU; нужны условия драйвера/прав. С PHP 8.5.0.
TCP_FUNCTION_BLKFreeBSD SOL_TCP: set строкой имени TCP stack/function block; get массив function_set_name/pcbcnt. PHP 8.5 проверяет длину/нулевые байты имени. С PHP 8.5.0.
TCP_FUNCTION_ALIASFreeBSD SOL_TCP чтение массива function_set_name/pcbcnt для alias выбранного TCP stack. Не пользовательский PHP callback. С PHP 8.5.0.
TCP_REUSPORT_LB_NUMAFreeBSD SOL_TCP, int: NUMA-affinity load-balanced reuseport; точное имя содержит REUSPORT. Значения включают специальные NODOM/CURDOM. С PHP 8.5.0.
TCP_REUSPORT_LB_NUMA_NODOMЗначение TCP_REUSPORT_LB_NUMA: не связывать с NUMA domain. С PHP 8.5.0.
TCP_REUSPORT_LB_NUMA_CURDOMЗначение TCP_REUSPORT_LB_NUMA: использовать текущий NUMA domain. С PHP 8.5.0.
TCP_BBR_ALGORITHMFreeBSD SOL_TCP идентификатор BBR алгоритма. Конкретные значения/применимость определяет установленный TCP stack; не универсальный Linux переключатель BBR. С PHP 8.5.0.

IP, multicast и UDP-Lite

Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.

Константа (int)Роль, значение и ограничение
UDPLITE_RECV_CSCOVSOL_UDPLITE, int байтов: минимальный охват checksum входящих датаграмм; 0 требует полный охват, положительный минимум 8 (заголовок). С PHP 8.3.0.
UDPLITE_SEND_CSCOVSOL_UDPLITE, int байтов: охват checksum при отправке; 0 — весь пакет, значение >=8 задаёт частичный охват. Поддержка протокола обязательна. С PHP 8.3.0.
IP_DONTFRAGFreeBSD IPPROTO_IP, int 0/1: запрет IPv4 фрагментации исходящего пакета; слишком большой пакет может дать ошибку. С PHP 8.3.0.
IP_MTU_DISCOVERLinux IPPROTO_IP, int IP_PMTUDISC_*: политика Path MTU Discovery. Согласуйте размеры датаграмм и обработку EMSGSIZE. С PHP 8.3.0.
IP_PMTUDISC_DOЗначение IP_MTU_DISCOVER: всегда использовать PMTU discovery/DF, не разрешая фрагментацию. С PHP 8.3.0.
IP_PMTUDISC_DONTЗначение IP_MTU_DISCOVER: отключить PMTU discovery, разрешить обычную фрагментацию. С PHP 8.3.0.
IP_PMTUDISC_WANTЗначение IP_MTU_DISCOVER: следовать политике PMTU, заданной маршрутом. С PHP 8.3.0.
IP_PMTUDISC_PROBEЗначение IP_MTU_DISCOVER: выставить DF и игнорировать известный path MTU для probing. С PHP 8.3.0.
IP_PMTUDISC_INTERFACEЗначение IP_MTU_DISCOVER: ориентироваться на MTU интерфейса, не path MTU; специальный Linux режим. С PHP 8.3.0.
IP_PMTUDISC_OMITЗначение IP_MTU_DISCOVER: режим как INTERFACE с дополнительным разрешением фрагментации; Linux. С PHP 8.3.0.
IP_BIND_ADDRESS_NO_PORTLinux IPPROTO_IP, int 0/1 до bind: не резервировать ephemeral port при bind(address,0), выбрать его позже при connect. С PHP 8.3.0.
IPV6_HOPLIMITIPv6 ancillary type: int hop limit конкретного пакета; приём запрашивают IPV6_RECVHOPLIMIT.
IPV6_MULTICAST_HOPSIPPROTO_IPV6, int: hop limit multicast, -1 выбирает default, допустимый явный предел 0…255.
IPV6_MULTICAST_IFIPPROTO_IPV6: выходной multicast интерфейс, в PHP индекс/имя; 0 выбирает маршрут ОС.
IPV6_MULTICAST_LOOPIPPROTO_IPV6, int 0/1: локальный multicast loopback; детали влияния sender/receiver зависят от ОС.
IPV6_PKTINFOIPv6 ancillary type: addr/ifindex для адреса/интерфейса пакета. Чтобы получать, включают IPV6_RECVPKTINFO.
IPV6_RECVHOPLIMITIPPROTO_IPV6, int 0/1: включить получение ancillary IPV6_HOPLIMIT через recvmsg.
IPV6_RECVPKTINFOIPPROTO_IPV6, int 0/1: получать ancillary IPV6_PKTINFO с адресом и интерфейсом.
IPV6_RECVTCLASSIPPROTO_IPV6, int 0/1: получать ancillary IPV6_TCLASS.
IPV6_TCLASSIPPROTO_IPV6/ancillary, int: IPv6 traffic class; не абсолютный приоритет доставки приложения.
IPV6_UNICAST_HOPSIPPROTO_IPV6, int: hop limit unicast, -1 — default, 0…255 — явное значение.
IPV6_V6ONLYIPPROTO_IPV6, int 0/1 обычно до bind: IPv6-only либо разрешение IPv4-mapped по возможностям ОС. Default различается между системами.
IP_MULTICAST_IFIPPROTO_IP: выходной multicast интерфейс. PHP принимает индекс/имя, а не C-структуру адреса; 0 — выбор ОС.
IP_MULTICAST_LOOPIPPROTO_IP, int 0/1: локальный multicast loopback. Winsock применяет настройку на пути приёма, BSD-подобные стеки — на пути отправки; это различие нельзя менять местами.
IP_MULTICAST_TTLIPPROTO_IP, int 0…255: TTL IPv4 multicast; обычный default 1, область зависит от маршрутизации сети.
IP_PORTRANGEBSD IPPROTO_IP, int IP_PORTRANGE_*: диапазон автоматического выбора локального порта. С PHP 8.4.0.
IP_PORTRANGE_DEFAULTЗначение IP_PORTRANGE: системный обычный диапазон ephemeral ports. С PHP 8.4.0.
IP_PORTRANGE_HIGHЗначение IP_PORTRANGE: системный высокий диапазон портов; точные границы задаёт ОС. С PHP 8.4.0.
IP_PORTRANGE_LOWЗначение IP_PORTRANGE: низкий привилегированный диапазон; обычно нужны дополнительные права. С PHP 8.4.0.
MCAST_BLOCK_SOURCESet-only IPPROTO_IP/IPV6: запретить source в уже принятой group; массив group/interface/source.
MCAST_JOIN_GROUPSet-only IPPROTO_IP/IPV6: присоединиться к group на interface, получая разрешённые источники. Массив group/interface; не запускает внешнюю отправку.
MCAST_JOIN_SOURCE_GROUPSet-only: присоединиться к group только для указанного source. Массив group/interface/source.
MCAST_LEAVE_GROUPSet-only: покинуть group на interface; не закрывает Socket. Требуется корректная структура и существующее членство по правилам ОС.
MCAST_LEAVE_SOURCE_GROUPSet-only: снять подписку group/source на interface.
MCAST_UNBLOCK_SOURCESet-only: снять прежнюю блокировку source в group/interface.
UDP_SEGMENTLinux SOL_UDP: размер полезной части сегмента GSO в байтах, native uint16. PHP 8.5 поддерживает этот размер; требуется поддержка ядра/стека, не обычная нарезка любой строки на независимые команды. С PHP 8.5.0.
IP_BINDANYFreeBSD/NetBSD/OpenBSD IPPROTO_IP, int 0/1: разрешить bind нелокального адреса, с системными правами. Не даёт маршрут и не делает удалённый адрес собственным. С PHP 8.5.0.

Ancillary credentials, descriptor и filters

Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.

Константа (int)Роль, значение и ограничение
SCM_RIGHTSSOL_SOCKET control type: передача открытых дескрипторов через Unix socket. data — непустой список Socket/stream, нужен обычный байт; не поддержано Windows.
SCM_CREDENTIALSSOL_SOCKET control type с pid/uid/gid процесса, поддержка/проверка ядром ОС. Не доверяйте произвольному JSON с такими ключами.
SCM_CREDSПлатформенное credentials control type (BSD). Его native структура отличается от Linux SCM_CREDENTIALS. С PHP 8.2.0.
SCM_CREDS2FreeBSD расширенный credentials control type для постоянной передачи учётных данных. Используется вместе с LOCAL_CREDS_PERSISTENT по договору ОС. С PHP 8.2.0.
LOCAL_CREDSSOL_LOCAL, int 0/1: включить credentials ancillary для локальных сообщений на поддержанной BSD системе. С PHP 8.2.0.
LOCAL_CREDS_PERSISTENTSOL_LOCAL, int 0/1: запрашивать credentials не только один раз, но постоянно; FreeBSD, структуры SCM_CREDS2. С PHP 8.2.0.
SKF_AD_OFFLinux classic BPF: базовое отрицательное смещение для ancillary load, а не обычный byte offset пакета. Используется в native BPF инструкции. С PHP 8.2.0.
SKF_AD_PROTOCOLLinux BPF ancillary selector: Ethernet protocol пакета. С PHP 8.2.0.
SKF_AD_PKTTYPELinux BPF ancillary selector: тип пакета относительно локального узла (host/broadcast/multicast и др.). С PHP 8.2.0.
SKF_AD_IFINDEXLinux BPF ancillary selector: индекс сетевого интерфейса пакета. С PHP 8.2.0.
SKF_AD_NLATTRLinux BPF extension: поиск netlink attribute в данных сообщения. С PHP 8.2.0.
SKF_AD_NLATTR_NESTLinux BPF extension: поиск вложенного netlink attribute. С PHP 8.2.0.
SKF_AD_MARKLinux BPF ancillary selector: packet mark, не пользовательская строковая метка. С PHP 8.2.0.
SKF_AD_QUEUELinux BPF ancillary selector: mapping аппаратной/системной очереди пакета. С PHP 8.2.0.
SKF_AD_HATYPELinux BPF ancillary selector: тип hardware link устройства. С PHP 8.2.0.
SKF_AD_RXHASHLinux BPF ancillary selector: receive hash потока, вычисленный стеком/устройством. С PHP 8.2.0.
SKF_AD_CPULinux BPF ancillary selector: текущий CPU обработки пакета. С PHP 8.2.0.
SKF_AD_ALU_XOR_XLinux classic BPF extension для операции XOR аккумулятора с регистром X. С PHP 8.2.0.
SKF_AD_VLAN_TAGLinux BPF ancillary selector: VLAN tag, сохранённый в metadata. С PHP 8.2.0.
SKF_AD_VLAN_TAG_PRESENTLinux BPF ancillary selector: признак наличия VLAN tag; нулевой tag не эквивалентен отсутствию. С PHP 8.2.0.
SKF_AD_PAY_OFFSETLinux BPF ancillary selector: вычисленное смещение полезной нагрузки пакета. С PHP 8.2.0.
SKF_AD_RANDOMLinux BPF ancillary selector: системное псевдослучайное значение для обработки пакета, не источник токенов PHP. С PHP 8.2.0.
SKF_AD_VLAN_TPIDLinux BPF ancillary selector: тип протокола VLAN tag. С PHP 8.2.0.
SKF_AD_MAXГраница диапазона Linux BPF ancillary extensions, а не самостоятельное поле пакета. С PHP 8.2.0.
FIL_ATTACHSOL_FILTER на Illumos/Solaris: присоединить named socket filter. Нужен установленный системный фильтр и соответствующий native договор.
FIL_DETACHSOL_FILTER на Illumos/Solaris: отсоединить named socket filter, не закрывая endpoint.

Системные SOCKET_* коды и исправления имён

Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.

Большинство редких кодов связано с системными Unix/STREAMS/устройствами и не возникает в обычном TCP клиенте. Не проверяйте все их подряд; обрабатывайте ожидаемые коды вашей операции, остальные логируйте и корректно завершайте. Ошибки имён в PHP.net: ETOOMYREFS → ETOOMANYREFS, EDUOT → EDQUOT, EPROTOOPT → ENOPROTOOPT, ADDRINUSE → EADDRINUSE; неверные записи не объявляются нами как PHP константы.

Константа (int)Роль, значение и ограничение
SOCKET_EINTRСистемный вызов прерван сигналом; решение о повторе зависит от операции и оставшегося срока.
SOCKET_EBADFНеверный/закрытый descriptor; исправьте владение, а не повторяйте бесконечно.
SOCKET_EACCESДоступ запрещён: проверьте права адреса, raw socket, firewall/policy.
SOCKET_EFAULTНедопустимый адрес памяти системного аргумента; для корректной PHP обёртки не обычная сетевая ситуация.
SOCKET_EINVALАргумент/состояние не подходит операции; проверьте семейство, флаги и порядок вызовов.
SOCKET_EMFILEПроцесс исчерпал descriptors; закройте утечки и ограничьте параллелизм.
SOCKET_ENAMETOOLONGСлишком длинное имя/путь, в том числе локального socket-файла.
SOCKET_ENOTEMPTYКаталог не пуст при файловой операции, связанной с локальным адресом.
SOCKET_ELOOPСлишком много переходов по символическим ссылкам в пути.
SOCKET_EWOULDBLOCKОперация сейчас заблокировалась бы: ожидайте готовность, не busy-loop. Часто равен EAGAIN, но проверяйте определённые имена.
SOCKET_EREMOTEОбъект удалённый по системному файловому/сетевому договору.
SOCKET_EUSERSДостигнут системный предел пользователей/контекстов.
SOCKET_ENOTSOCKОперация sockets применена к descriptor другого типа.
SOCKET_EDESTADDRREQНе задан адрес назначения: нужен connect либо sendto/name.
SOCKET_EMSGSIZEСообщение слишком велико для протокола/MTU; не делите UDP молча без своего протокола сборки.
SOCKET_EPROTOTYPEТип endpoint не соответствует выбранному протоколу.
SOCKET_EPROTONOSUPPORTПротокол не поддержан системой.
SOCKET_ESOCKTNOSUPPORTТип Socket не поддержан для выбранного семейства.
SOCKET_EOPNOTSUPPОперация не поддерживается этим endpoint/протоколом.
SOCKET_EPFNOSUPPORTСемейство протоколов не поддержано.
SOCKET_EAFNOSUPPORTСемейство адресов не подходит протоколу/операции.
SOCKET_EADDRNOTAVAILЗапрошенный локальный адрес отсутствует/не может быть назначен.
SOCKET_ENETDOWNСетевой интерфейс/подсистема недоступны.
SOCKET_ENETUNREACHНет маршрута в нужную сеть.
SOCKET_ENETRESETСоединение потеряно после сетевого сброса.
SOCKET_ECONNABORTEDСоединение прекращено локальной сетевой подсистемой.
SOCKET_ECONNRESETPeer сбросил соединение; прежнюю сессию продолжать нельзя.
SOCKET_ENOBUFSЯдру не хватает буферов/сетевых ресурсов.
SOCKET_EISCONNEndpoint уже соединён, а операция требует другого состояния.
SOCKET_ENOTCONNEndpoint ещё не соединён/уже разъединён.
SOCKET_ESHUTDOWNЗапрещена отправка после shutdown соответствующей стороны.
SOCKET_ETIMEDOUTИстёк системный срок операции; не доказательство, что peer не обработал ранее отправленную команду.
SOCKET_ECONNREFUSEDПодключение отвергнуто, например нет listener; не равнозначно DNS failure.
SOCKET_EHOSTDOWNУзел признан недоступным/выключенным системным стеком.
SOCKET_EHOSTUNREACHНе найден путь к узлу.
SOCKET_EALREADYПредыдущая неблокирующая операция ещё выполняется.
SOCKET_EINPROGRESSНеблокирующее подключение началось и ещё не завершилось; select + SO_ERROR определяют исход.
SOCKET_ENOPROTOOPTОпция недоступна на данном уровне/протоколе. Имя существует и на Unix, не только Windows.
SOCKET_EADDRINUSEАдрес/порт занят; проверьте уже работающий listener и правила reuse. Существует также Unix.
SOCKET_ETOOMYREFSОпечатка в текущем списке PHP.net; корректное системное/PHP имя SOCKET_ETOOMANYREFS. Не копируйте ошибочное имя в код.
SOCKET_EPROCLIMWindows: предел процессов/задач провайдера sockets.
SOCKET_EDUOTОпечатка текущего списка PHP.net; корректное имя SOCKET_EDQUOT (превышена квота).
SOCKET_ESTALEУстаревшая ссылка/handle на объект системного хранилища.
SOCKET_EDISCONWindows: корректное завершение соединения, сообщённое провайдером.
SOCKET_SYSNOTREADYWindows: сетевая подсистема ещё не готова.
SOCKET_VERNOTSUPPORTEDWindows: нужная версия Winsock не поддержана.
SOCKET_NOTINITIALISEDWindows: Winsock не инициализирован; обычно это проблема среды/расширения, не PHP connect-параметра.
SOCKET_HOST_NOT_FOUNDResolver Windows не нашёл узел; не переносите автоматически на false socket_addrinfo_lookup.
SOCKET_TRY_AGAINВременный отказ resolver Windows; повтор допустим только с ограничением срока/попыток.
SOCKET_NO_RECOVERYResolver Windows сообщает невосстановимый отказ.
SOCKET_NO_DATAResolver нашёл имя, но нет запрошенных данных/записи.
SOCKET_NO_ADDRESSИсторический resolver alias отсутствия адреса; зависит от платформы, часто совпадает с NO_DATA.
SOCKET_EPERMОперация не разрешена политикой/правами ОС.
SOCKET_ENOENTНет файла/компонента пути локального endpoint.
SOCKET_EIOОшибка системного ввода/вывода.
SOCKET_ENXIOНет указанного устройства/адреса.
SOCKET_E2BIGСистемный список аргументов слишком велик.
SOCKET_EAGAINВременно нет ресурса/готовности; для non-blocking чтения часто означает то же, что EWOULDBLOCK.
SOCKET_ENOMEMНедостаточно памяти ОС.
SOCKET_ENOTBLKОжидалось блочное устройство, получен другой объект.
SOCKET_EBUSYУстройство/ресурс занят.
SOCKET_EEXISTОбъект/локальный путь уже существует.
SOCKET_EXDEVОперация затронула разные файловые устройства и не поддерживает такой переход.
SOCKET_ENODEVНет требуемого устройства.
SOCKET_ENOTDIRКомпонент пути не каталог.
SOCKET_EISDIRПолучен каталог вместо допустимого файла/объекта.
SOCKET_ENFILEОбщесистемная таблица открытых файлов исчерпана.
SOCKET_ENOTTYОперация ioctl не подходит устройству; историческое имя не означает ошибку текста PHP.
SOCKET_ENOSPCНет места на устройстве/в соответствующем системном ресурсе.
SOCKET_ESPIPEНедопустимое seek для потокового объекта; sockets не позиционируются как обычные файлы.
SOCKET_EROFSФайловая система доступна только для чтения.
SOCKET_EMLINKСлишком много жёстких ссылок.
SOCKET_EPIPEЗапись в закрытую сторону канала; на Unix возможен SIGPIPE, если не подавлен выбранным механизмом.
SOCKET_ENOLCKНет доступных системных блокировок.
SOCKET_ENOSYSСистемная функция не реализована.
SOCKET_ENOMSGНет сообщения требуемого типа в системной очереди.
SOCKET_EIDRMСистемный IPC идентификатор удалён.
SOCKET_ECHRNGНомер канала вне допустимого диапазона.
SOCKET_EL2NSYNCКанал уровня 2 не синхронизирован.
SOCKET_EL3HLTУровень 3 остановлен.
SOCKET_EL3RSTУровень 3 сброшен.
SOCKET_ELNRNGНомер link вне диапазона.
SOCKET_EUNATCHДрайвер протокола не подключён.
SOCKET_ENOCSIНет требуемой CSI-структуры устройства.
SOCKET_EL2HLTУровень 2 остановлен.
SOCKET_EBADEНедопустимый системный обмен.
SOCKET_EBADRНекорректный descriptor системного запроса.
SOCKET_EXFULLСистемный обмен/очередь переполнен.
SOCKET_ENOANOНет системного anode; редкий исторический код устройства.
SOCKET_EBADRQCНедопустимый код системного запроса.
SOCKET_EBADSLTНедопустимый системный slot.
SOCKET_ENOSTRУстройство не является STREAMS endpoint.
SOCKET_ENODATAВ системном STREAMS контексте нет данных; не универсальный эквивалент TCP EOF.
SOCKET_ETIMEИстёк системный таймер, отличается от ETIMEDOUT по подсистеме.
SOCKET_ENOSRНет ресурсов STREAMS.
SOCKET_ENONETМашина не подключена к требуемой сети.
SOCKET_ENOLINKСетевой link разорван.
SOCKET_EADVИсторическая ошибка advertise удалённого файлового ресурса.
SOCKET_ESRMNTИсторическая ошибка server-side remote mount.
SOCKET_ECOMMОшибка связи при отправке.
SOCKET_EPROTOНарушение системного протокола.
SOCKET_EMULTIHOPОперация требует неподдержанного многопереходного обращения.
SOCKET_EBADMSGНекорректное системное сообщение/его тип.
SOCKET_ENOTUNIQИмя в сети не уникально.
SOCKET_EBADFDDescriptor существует, но находится в недопустимом состоянии.
SOCKET_EREMCHGУдалённый адрес изменился.
SOCKET_ERESTARTПрерванный системный вызов требует перезапуска по правилам подсистемы.
SOCKET_ESTRPIPEОшибка STREAMS pipe.
SOCKET_EPROTOOPTОпечатка/историческая запись списка PHP.net: используйте подтверждённое SOCKET_ENOPROTOOPT, а не предполагайте наличие этого имени.
SOCKET_ADDRINUSEОпечатка списка PHP.net: корректное PHP имя SOCKET_EADDRINUSE.
SOCKET_ETOOMANYREFSСлишком много ссылок на системный объект, например descriptor передачи; освободите ненужные handles.
SOCKET_EISNAMОбъект является специальным именованным файлом.
SOCKET_EREMOTEIOОшибка удалённого ввода/вывода.
SOCKET_EDQUOTПревышена системная квота.
SOCKET_ENOMEDIUMНе найден носитель устройства.
SOCKET_EMEDIUMTYPEНеподходящий тип носителя.

Практика, выбор API и следующий шаг

Практика 1: измените товар A42 на B17 и число 2 на 3 в локальной TCP команде. Ожидается accepted:B17:3, а неожиданная команда должна отвергаться. Практика 2: разрежьте JSON frame на несколько записей и убедитесь, что получатель всё равно читает заголовок и тело до точной длины. Практика 3: задайте в заголовке 2048, не отправляя тело: проверка должна отказать до чтения payload. Практика 4: добавьте UDP номер измерения и отбрасывайте повтор номера без повторного побочного действия. Практика 5: в deadline-примере завершите отправку после двух байтов — ожидается ошибка неполного кадра, а не зависание.

Для решения сперва выберите карточку конкретной стадии: создание/адрес → bind/listen/accept либо connect → send/read → проверка длины/ошибки → shutdown/close. AddressInfo и aliases не меняют этот порядок. Расширение sockets не реализует за вас HTTP, TLS, WebSocket framing, авторизацию или повторяемость операций. Сетевые функции Core и streams разобраны отдельно: socket_set_timeout/socket_set_blocking/socket_get_status — имена stream aliases, их нельзя передавать объекту Socket как замену этой категории.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

socket_createПодготовить конечную точку, через которую программа будет обмениваться байтами или сообщениями.

PHP 4.1.0+ · sockets

socket_create(int $domain, int $type, int $protocol): Socket|false

Принять локальный запрос товара и вернуть его остаток. Создаёт конечную точку IPv4/TCP. Она ещё не привязана и не соединена; Socket передаётся следующим операциям.

socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Принять локальный запрос товара и вернуть его остатокСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$listener = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($listener === false) { throw new RuntimeException('Не создан слушатель'); }
if (!socket_bind($listener, '127.0.0.1', 0)) { throw new RuntimeException('Адрес не привязан'); }
if (!socket_listen($listener, 2)) { throw new RuntimeException('Очередь не создана'); }
if (!socket_getsockname($listener, $address, $port)) { throw new RuntimeException('Адрес не прочитан'); }
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($client === false || !socket_connect($client, $address, $port)) { throw new RuntimeException('Клиент не подключён'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Порядок явный: сервер create→bind→listen, клиент create→connect, сервер accept. Далее writeBytes посылает product:1, serveProduct выбирает строку из заданного каталога, readCatalogLine читает полный ответ. Полный код трёх помощников есть в local-net.php.

Ошибки и следующий шаг. Эта однопроцессная последовательность годится для малого локального опыта: данные передаются по шагам. Публичному серверу нужны ограничения клиентов, общего срока, аутентификация и модель конкурентного обслуживания. Успешный connect не доказывает выполнение прикладной команды.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Подготовить конечную точку, через которую программа будет обмениваться байтами или сообщениями.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
domainСемейство адреса AF_INET/AF_INET6/AF_UNIX; PHP 8.5 также AF_PACKET при поддержке Linux.
typeСпособ связи; наличие имени не гарантирует поддержку выбранного сочетания ОС.
protocolНомер протокола или 0 для default; SOL_TCP/SOL_UDP для явного выбора.
РезультатSocket (с PHP 8; прежде resource) либо false + системная ошибка.
ОшибкаWarning/false при системном отказе; TypeError/ValueError при ошибке договора. socket_last_error() без объекта нужен, когда объекта ещё нет.
Создание TCP endpoint
  1. AF_INET → IPv4
  2. SOCK_STREAM → поток
  3. SOL_TCP → TCP
  4. Socket создан, адрес ещё не назначен
  5. socket_close освобождает его

Этот вариант. AF_INET выбирает семейство IPv4, SOCK_STREAM — упорядоченный поток байтов, SOL_TCP — TCP. Создание endpoint само не делает его сервером и не подключает к адресу. instanceof проверяет класс, socket_close освобождает endpoint.

Создать TCP endpoint, ещё никуда не подключаясьСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) throw new RuntimeException(socket_strerror(socket_last_error()));
echo $socket instanceof Socket ? "Создан Socket для IPv4 TCP\n" : "Неожиданный результат\n";
socket_close($socket);
Ожидаемый результат
Создан Socket для IPv4 TCP

Этот вариант. Функция возвращает открытый объект, которым теперь владеет вызывающий код. UDP сохраняет границы сообщений, но не гарантирует доставку/порядок. Создание не отправило показание.

Выделить создание UDP endpoint для учебного датчикаСамостоятельный пример
<?php
declare(strict_types=1);

function createTelemetrySocket(): Socket {
    $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    if ($socket === false) throw new RuntimeException(socket_strerror(socket_last_error()));
    return $socket;
}
$socket = createTelemetrySocket();
try {
    echo socket_get_option($socket, SOL_SOCKET, SO_TYPE) === SOCK_DGRAM
        ? "Готов endpoint для отдельных UDP сообщений\n" : "Неверный тип\n";
} finally { socket_close($socket); }
Ожидаемый результат
Готов endpoint для отдельных UDP сообщений

Доступные типы: STREAM — надёжный двусторонний поток (обычно TCP); DGRAM — сообщения без гарантии доставки (обычно UDP); SEQPACKET — надёжные сообщения с границами; RAW — низкоуровневые пакеты с правами ОС; RDM — надёжные datagrams без гарантии порядка, часто не реализованы. AF_UNIX служит локальному IPC, AF_INET6 — IPv6. AF_PACKET поддержан PHP 8.5 на Linux для уровня 2 и обычно требует специальных прав; он не является переносимой заменой TCP.

Протокол 0 просит подходящий default ОС для выбранного сочетания; для TCP/UDP понятнее SOL_TCP/SOL_UDP. Номер иного протокола можно получить getprotobyname. Биты SOCK_NONBLOCK/SOCK_CLOEXEC с PHP 8.4 доступны только где их предоставляет ОС. В актуальном PHP 8.5 неверное семейство и явно недопустимый тип могут дать ValueError: старое описание PHP.net о warning и молчаливой подстановке AF_INET/SOCK_STREAM не следует применять к этой версии. Остальные неподдержанные сочетания обычно дают warning + false.

Связанные действия: socket_bind, socket_connect, socket_close.

socket_bindНазначить собственный адрес endpoint: где принимать данные или с какого интерфейса подключаться.

PHP 4.1.0+ · sockets

socket_bind(Socket $socket, string $address, int $port = 0): bool

Принять локальный запрос товара и вернуть его остаток. Закрепляет за слушателем собственный адрес. Порт 0 поручает ОС выбрать свободный порт, который затем читают через getsockname.

socket_bind($listener, '127.0.0.1', 0)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Принять локальный запрос товара и вернуть его остатокСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$listener = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($listener === false) { throw new RuntimeException('Не создан слушатель'); }
if (!socket_bind($listener, '127.0.0.1', 0)) { throw new RuntimeException('Адрес не привязан'); }
if (!socket_listen($listener, 2)) { throw new RuntimeException('Очередь не создана'); }
if (!socket_getsockname($listener, $address, $port)) { throw new RuntimeException('Адрес не прочитан'); }
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($client === false || !socket_connect($client, $address, $port)) { throw new RuntimeException('Клиент не подключён'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Порядок явный: сервер create→bind→listen, клиент create→connect, сервер accept. Далее writeBytes посылает product:1, serveProduct выбирает строку из заданного каталога, readCatalogLine читает полный ответ. Полный код трёх помощников есть в local-net.php.

Ошибки и следующий шаг. Эта однопроцессная последовательность годится для малого локального опыта: данные передаются по шагам. Публичному серверу нужны ограничения клиентов, общего срока, аутентификация и модель конкурентного обслуживания. Успешный connect не доказывает выполнение прикладной команды.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Назначить собственный адрес endpoint: где принимать данные или с какого интерфейса подключаться.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketСозданный и ещё не соединённый/не слушающий Socket.
addressЛокальный IPv4/IPv6 адрес либо путь AF_UNIX; не URL.
port0…65535 для IP. 0 — свободный порт ОС; не число клиентов.
Результатtrue либо false с warning; номер ошибки у этого socket.
ВерсииPHP 8: объект; PHP 8.5: ValueError для порта вне диапазона.
Локальный UDP адрес
  1. Создан UDP endpoint
  2. bind 127.0.0.1:0
  3. ОС выбирает свободный порт
  4. getsockname сообщает фактический адрес

Этот вариант. Порт 0 при bind означает «выбрать свободный», не порт удалённого сервиса. Точное число меняется, поэтому проверяем свойство. 127.0.0.1 разрешает локальный обмен; 0.0.0.0 имело бы другую область доступа.

Выделить свободный локальный порт без угадывания числаСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет UDP endpoint');
try {
    if (!socket_bind($socket, '127.0.0.1', 0)) throw new RuntimeException(socket_strerror(socket_last_error($socket)));
    if (!socket_getsockname($socket, $address, $port)) throw new RuntimeException('Нет адреса');
    echo $address, "\n", $port > 0 ? "Свободный порт назначен ОС\n" : "Порт не назначен\n";
} finally { socket_close($socket); }
Ожидаемый результат
127.0.0.1
Свободный порт назначен ОС

Этот вариант. Граница проверяется как ValueError с PHP 8.5. Для порта из формы сначала нужна прикладная валидация; этот catch показывает защиту API, а не полноценную обработку HTTP-ввода.

Остановить ошибочный номер порта до системного bindСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет сокета');
try {
    try { socket_bind($socket, '127.0.0.1', 65536); }
    catch (ValueError $error) { echo "Порт должен помещаться в 0…65535\n"; }
} finally { socket_close($socket); }
Ожидаемый результат
Порт должен помещаться в 0…65535

bind выбирает местную сторону, а connect — удалённую. Клиенту обычно не нужен явный bind: ОС выбирает исходящий адрес/порт. Если он нужен для определённого интерфейса, вызовите bind до connect. Для AF_UNIX address — путь сокета, port не применяется; существующий путь, права каталога и уборка файла требуют отдельного решения. Не удаляйте чужой socket-файл перед bind. Для AF_INET6 адрес задаётся без квадратных скобок URL.

Связанные действия: socket_getsockname, socket_listen, socket_connect.

socket_listenРазрешить входящие соединения на привязанном endpoint и подготовить очередь до accept.

PHP 4.1.0+ · sockets

socket_listen(Socket $socket, int $backlog = 0): bool

Принять локальный запрос товара и вернуть его остаток. Включает очередь входящих TCP-соединений после bind. Число 2 — ограничение ожидающих соединений для данного опыта, не число PHP-workers.

socket_listen($listener, 2)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Принять локальный запрос товара и вернуть его остатокСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$listener = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($listener === false) { throw new RuntimeException('Не создан слушатель'); }
if (!socket_bind($listener, '127.0.0.1', 0)) { throw new RuntimeException('Адрес не привязан'); }
if (!socket_listen($listener, 2)) { throw new RuntimeException('Очередь не создана'); }
if (!socket_getsockname($listener, $address, $port)) { throw new RuntimeException('Адрес не прочитан'); }
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($client === false || !socket_connect($client, $address, $port)) { throw new RuntimeException('Клиент не подключён'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Порядок явный: сервер create→bind→listen, клиент create→connect, сервер accept. Далее writeBytes посылает product:1, serveProduct выбирает строку из заданного каталога, readCatalogLine читает полный ответ. Полный код трёх помощников есть в local-net.php.

Ошибки и следующий шаг. Эта однопроцессная последовательность годится для малого локального опыта: данные передаются по шагам. Публичному серверу нужны ограничения клиентов, общего срока, аутентификация и модель конкурентного обслуживания. Успешный connect не доказывает выполнение прикладной команды.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Разрешить входящие соединения на привязанном endpoint и подготовить очередь до accept.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketSocket после create + bind либо addrinfo_bind.
backlogЖелаемая длина очереди ожидающих соединений; ОС может ограничить. Default этой функции 0, в отличие от create_listen.
Результатtrue — слушающий режим включён; false + warning — отказ. Это ещё не принятый клиент.
Подготовка локального listener
  1. create → TCP endpoint
  2. bind → 127.0.0.1 и свободный порт
  3. listen(2) → очередь ожидания
  4. accept позже вернёт отдельный клиентский Socket

Этот вариант. listen возвращает управление сразу: он включает пассивный режим, а не выполняет бесконечный цикл. backlog=2 относится к очереди ещё не принятых соединений.

Перевести привязанный TCP endpoint в режим ожиданияСамостоятельный пример
<?php
declare(strict_types=1);

$listener = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($listener === false) throw new RuntimeException('Нет endpoint');
try {
    if (!socket_bind($listener, '127.0.0.1', 0)) throw new RuntimeException('Не удалось привязать адрес');
    if (!socket_listen($listener, 2)) throw new RuntimeException('Не удалось открыть очередь');
    echo "Локальный listener готов; клиентов ещё не принимали\n";
} finally { socket_close($listener); }
Ожидаемый результат
Локальный listener готов; клиентов ещё не принимали

Этот вариант. Два client endpoint созданы локально. После accept соединение выходит из очереди ожидания; backlog не является пределом общего числа обслуженных клиентов. Распаковка ... передаёт элементы массива отдельными аргументами. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Принять два подключения из очередиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
$listener = labListener();
socket_getsockname($listener, $address, $port);
$clients = []; $accepted = [];
try {
    for ($i = 0; $i < 2; $i++) {
        $client = labSocket(AF_INET, SOCK_STREAM, SOL_TCP);
        if (!socket_connect($client, $address, $port)) throw new RuntimeException('Нет подключения');
        $clients[] = $client;
    }
    for ($i = 0; $i < 2; $i++) {
        $peer = socket_accept($listener);
        if ($peer === false) throw new RuntimeException('Нет клиента в очереди');
        $accepted[] = $peer;
    }
    echo 'Принято клиентов: ', count($accepted), "\n";
} finally { labClose(...[...$accepted, ...$clients, $listener]); }
Ожидаемый результат
Принято клиентов: 2

Применяется к STREAM/SEQPACKET, а не к обычному UDP. ОС ограничивает очередь своей политикой: Linux ограничивает системным максимумом, Windows при SOMAXCONN выбирает разумный предел provider. Реальный размер нельзя переносимо прочитать обратно. Переполненная очередь может дать отказ или ожидание/повтор SYN, а не ровно одинаковую ошибку на всех ОС.

Связанные действия: socket_accept, socket_bind, socket_create_listen.

socket_acceptПолучить отдельный endpoint для одного клиента, сохранив listener для следующих подключений.

PHP 4.1.0+ · sockets

socket_accept(Socket $socket): Socket|false

Принять локальный запрос товара и вернуть его остаток. Возвращает отдельный Socket принятого соединения. Слушатель остаётся принимать другие подключения; запрос/ответ идут через peer, не через listener.

socket_accept($listener)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Принять локальный запрос товара и вернуть его остатокСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$listener = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($listener === false) { throw new RuntimeException('Не создан слушатель'); }
if (!socket_bind($listener, '127.0.0.1', 0)) { throw new RuntimeException('Адрес не привязан'); }
if (!socket_listen($listener, 2)) { throw new RuntimeException('Очередь не создана'); }
if (!socket_getsockname($listener, $address, $port)) { throw new RuntimeException('Адрес не прочитан'); }
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($client === false || !socket_connect($client, $address, $port)) { throw new RuntimeException('Клиент не подключён'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Порядок явный: сервер create→bind→listen, клиент create→connect, сервер accept. Далее writeBytes посылает product:1, serveProduct выбирает строку из заданного каталога, readCatalogLine читает полный ответ. Полный код трёх помощников есть в local-net.php.

Ошибки и следующий шаг. Эта однопроцессная последовательность годится для малого локального опыта: данные передаются по шагам. Публичному серверу нужны ограничения клиентов, общего срока, аутентификация и модель конкурентного обслуживания. Успешный connect не доказывает выполнение прикладной команды.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Получить отдельный endpoint для одного клиента, сохранив listener для следующих подключений.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketСлушающий STREAM/SEQPACKET после bind/listen.
РезультатНовый Socket (PHP 8+) либо false. При нескольких ожидающих выбирается очередное соединение.
ОтказWarning + false; при non-blocking отсутствие клиента — нормальный временный EWOULDBLOCK/EAGAIN. Для accept немедленно читать глобальный socket_last_error() без аргумента: listener не получает ошибку неудавшегося нового endpoint.
Клиент A42
  1. Listener уже слушает localhost
  2. Клиент соединяется
  3. accept создаёт отдельный $peer
  4. A42 читается из $peer
  5. Listener остаётся отдельным объектом

Этот вариант. socket_accept работает с очередью listener. Отдельный $peer получает данные одного клиента; из него нельзя принимать другие соединения. Listener остаётся доступен до отдельного close. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Увидеть отдельный объект принятого соединенияСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
$listener = labListener();
socket_getsockname($listener, $address, $port);
$client = labSocket(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_connect($client, $address, $port)) throw new RuntimeException('Нет подключения');
$peer = socket_accept($listener);
if ($peer === false) throw new RuntimeException('Нет принятого соединения');
try {
    echo $peer !== $listener ? "Listener и принятый клиент — разные Socket\n" : "Ошибка\n";
    labWriteAll($client, 'A42');
    echo labReadExact($peer, 3), "\n";
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
Listener и принятый клиент — разные Socket
A42

Этот вариант. @ подавляет только ожидаемое предупреждение этого вызова, после него код немедленно проверяется. Для accept читаем глобальный socket_last_error() без listener: ошибка относится к попытке создать новый endpoint. false не всегда означает аварию: EWOULDBLOCK/EAGAIN сообщает отсутствие готового клиента. Вместо busy loop используйте select. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Не ждать клиента, которого ещё нетСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
$listener = labListener();
try {
    if (!socket_set_nonblock($listener)) throw new RuntimeException('Нет неблокирующего режима');
    $peer = @socket_accept($listener);
    if ($peer !== false) { socket_close($peer); throw new RuntimeException('Неожиданный клиент'); }
    $error = socket_last_error();
    $wouldBlock = $error === SOCKET_EWOULDBLOCK
        || (defined('SOCKET_EAGAIN') && $error === constant('SOCKET_EAGAIN'));
    echo $wouldBlock ? "Очередь пока пуста; можно ждать готовности\n" : "Другой отказ\n";
} finally { socket_close($listener); }
Ожидаемый результат
Очередь пока пуста; можно ждать готовности

По умолчанию accept блокируется при пустой очереди. В неблокирующем режиме подготовьте listener через socket_set_nonblock; для объекта Socket используйте именно функции этой книги. Упоминание socket_set_blocking в старом тексте относится к stream alias и не подходит для Socket. Режим принятого endpoint лучше задавать явно: наследование режима зависит от ОС.

Связанные действия: socket_listen, socket_set_nonblock, socket_select.

socket_connectНазначить удалённую сторону и установить TCP-соединение либо выбрать peer для UDP.

PHP 4.1.0+ · sockets

socket_connect(Socket $socket, string $address, ?int $port = null): bool

Принять локальный запрос товара и вернуть его остаток. Клиент соединяется с ранее открытым локальным слушателем. Для TCP устанавливается соединение; это ещё не отправка команды product:1.

socket_connect($client, $address, $port)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Принять локальный запрос товара и вернуть его остатокСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$listener = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($listener === false) { throw new RuntimeException('Не создан слушатель'); }
if (!socket_bind($listener, '127.0.0.1', 0)) { throw new RuntimeException('Адрес не привязан'); }
if (!socket_listen($listener, 2)) { throw new RuntimeException('Очередь не создана'); }
if (!socket_getsockname($listener, $address, $port)) { throw new RuntimeException('Адрес не прочитан'); }
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($client === false || !socket_connect($client, $address, $port)) { throw new RuntimeException('Клиент не подключён'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Порядок явный: сервер create→bind→listen, клиент create→connect, сервер accept. Далее writeBytes посылает product:1, serveProduct выбирает строку из заданного каталога, readCatalogLine читает полный ответ. Полный код трёх помощников есть в local-net.php.

Ошибки и следующий шаг. Эта однопроцессная последовательность годится для малого локального опыта: данные передаются по шагам. Публичному серверу нужны ограничения клиентов, общего срока, аутентификация и модель конкурентного обслуживания. Успешный connect не доказывает выполнение прикладной команды.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Назначить удалённую сторону и установить TCP-соединение либо выбрать peer для UDP.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketСозданный подходящий endpoint; bind при необходимости выполняется до connect.
addressIPv4, IPv6 без URL-скобок либо путь AF_UNIX. Не HTTP URL.
portПорт удалённой стороны, обязателен для IPv4/IPv6. Nullable с PHP 8 для иных семейств.
Результатtrue либо false. Для non-blocking false + pending-код отличается от окончательного отказа.
Локальное TCP подключение
  1. getsockname сообщает адрес listener
  2. socket_connect использует этот адрес
  3. accept получает второй конец
  4. Сервис посылает ready
  5. Клиент читает ровно 6 байтов

Этот вариант. Порт не задан заранее: он прочитан у собственного listener. connect устанавливает TCP-соединение, но не доказывает готовность прикладного протокола. Строка ready — отдельный договор нашего учебного сервиса. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Подключить клиента к собственному listenerСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
$listener = labListener();
socket_getsockname($listener, $address, $port);
$client = labSocket(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_connect($client, $address, $port)) throw new RuntimeException('Подключение не удалось');
$peer = socket_accept($listener);
if ($peer === false) throw new RuntimeException('Клиент не принят');
try {
    labWriteAll($peer, "ready\n");
    echo labReadExact($client, 6);
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
ready

Этот вариант. UDP connect не делает handshake TCP и не доказывает, что сервер существует. Он задаёт peer для send/recv и влияет на фильтрацию входящих datagrams. Один вызов send остаётся одним сообщением, а не надёжной очередью. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Выбрать адрес по умолчанию для UDPСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    if (!socket_connect($sender, '127.0.0.1', $port)) throw new RuntimeException('Нет UDP peer');
    if (socket_send($sender, 'T=21', 4, 0) !== 4) throw new RuntimeException('Сообщение не передано целиком');
    $data = '';
    if (socket_recvfrom($receiver, $data, 64, 0, $from, $fromPort) === false) throw new RuntimeException('Нет сообщения');
    echo $data, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
T=21

У non-blocking TCP connect возврат false с EINPROGRESS/EWOULDBLOCK может означать начало соединения. Дождитесь готовности записи через select и затем проверьте SO_ERROR; сама готовность записи не доказывает успех. Ограничивайте ожидание общим deadline. Для AF_INET/AF_INET6 порт обязателен даже при nullable-сигнатуре; для AF_UNIX адрес — путь и порт не используется.

Связанные действия: socket_getpeername, socket_select, socket_get_option.

socket_create_pairПолучить два уже соединённых локальных endpoint для обмена внутри программы или IPC.

PHP 4.1.0+ · sockets

socket_create_pair(int $domain, int $type, int $protocol, array &$pair): bool

Обменяться командой и ответом без выбора отдельного порта. Функция заполняет массив двумя уже соединёнными конечными точками. Windows требует поддерживаемого семейства AF_INET; Unix в этом опыте использует AF_UNIX.

socket_create_pair($family, SOCK_STREAM, 0, $pair)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Обменяться командой и ответом без выбора отдельного портаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$pair = [];
$family = PHP_OS_FAMILY === 'Windows' ? AF_INET : AF_UNIX;
if (!socket_create_pair($family, SOCK_STREAM, 0, $pair)) { throw new RuntimeException('Пара не создана'); }
[$client, $peer] = $pair;
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($client);
socket_close($peer);
Ожидаемый результат
Молоток — 8 шт.

Одна сторона играет клиента, другая — обработчик команды. Это настоящий обмен байтами внутри машины; массив pair содержит объекты, а не сам текст ответа.

Ошибки и следующий шаг. Пара не делает два фрагмента PHP параллельными. Если обе стороны начнут с блокирующего чтения, возникнет ожидание; здесь клиент сначала пишет короткую команду.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Получить два уже соединённых локальных endpoint для обмена внутри программы или IPC.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
domain/type/protocolСмысл как у create, но доступные пары ограничены реализацией ОС. Для переносимого учебного STREAM: AF_INET на Windows, AF_UNIX на Unix, protocol 0.
pairВыходной массив из двух Socket; передайте переменную, не литерал [].
Результатtrue/false; сами объекты выходят через pair. С PHP 8 элементы — объекты, прежде ресурсы.
Два конца одной пары
  1. Передан пустой $pair
  2. create_pair заполняет индексы 0 и 1
  3. Конец 0 пишет OK
  4. Конец 1 читает OK
  5. Закрыть нужно оба конца

Этот вариант. &$pair в сигнатуре означает выход через переданную переменную: return сообщает bool, а два Socket помещаются в массив. Для Windows используется AF_INET, для Unix — AF_UNIX. Маленький первый опыт явно проверяет полноту; универсальный код использует циклы.

Соединить две локальные конечные точки без внешнего сервераСамостоятельный пример
<?php
declare(strict_types=1);

$pair = [];
$family = PHP_OS_FAMILY === 'Windows' ? AF_INET : AF_UNIX;
if (!socket_create_pair($family, SOCK_STREAM, 0, $pair)) throw new RuntimeException('Нет пары');
try {
    $written = socket_write($pair[0], 'OK');
    if ($written !== 2) throw new RuntimeException('Неполная учебная запись');
    $reply = socket_read($pair[1], 2, PHP_BINARY_READ);
    if ($reply !== 'OK') throw new RuntimeException('Неполное чтение');
    echo $reply, "\n";
} finally { socket_close($pair[0]); socket_close($pair[1]); }
Ожидаемый результат
OK

Этот вариант. Оба конца равноправны и двунаправленны. В этом примере роли producer/worker выполняются последовательно в одном процессе; это не демонстрация параллельного выполнения. Для разных процессов требуется отдельная модель наследования/передачи endpoint. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Передать задание и получить подтверждение по той же пареСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$producer, $worker] = labPair();
try {
    labWriteAll($producer, 'order:42');
    echo 'Задание: ', labReadExact($worker, 8), "\n";
    labWriteAll($worker, 'accepted');
    echo 'Ответ: ', labReadExact($producer, 8), "\n";
} finally { labClose($producer, $worker); }
Ожидаемый результат
Задание: order:42
Ответ: accepted

Официальный IPC-пример с pcntl_fork относится к Unix и требует расширение PCNTL; он не переносится на Windows. В Windows существуют отдельные WSAPROTOCOL_INFO функции. После fork каждый процесс закрывает ненужную копию endpoint, иначе ожидаемый EOF может не прийти. Не заменяйте это сериализацией Socket.

Связанные действия: socket_write, socket_read, socket_wsaprotocol_info_export.

socket_create_listenСократить создание IPv4 listener, когда осознанно нужно слушать все местные интерфейсы.

PHP 4.1.0+ · sockets

socket_create_listen(int $port, int $backlog = SOMAXCONN): Socket|false

Понять сокращённое создание слушателя в изолированной среде. Сокращение создаёт IPv4/TCP-слушателя и привязывает его ко всем IPv4-интерфейсам. Оно не позволяет передать 127.0.0.1 как адрес.

socket_create_listen(0, 2)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Проверен только синтаксис; окружение этого опыта не запускалось. Архив содержит полный код и указанные файлы. Ожидаемый результат относится к описанной ниже среде. Сначала прочитайте требования платформы и README, затем подготовьте отдельный учебный стенд.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Понять сокращённое создание слушателя в изолированной средеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$listener = socket_create_listen(0, 2);
if ($listener === false) { throw new RuntimeException('Слушатель не создан'); }
if (!socket_getsockname($listener, $address, $port)) { throw new RuntimeException('Адрес не прочитан'); }
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($client === false || !socket_connect($client, '127.0.0.1', $port)) { throw new RuntimeException('Клиент не подключён'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Клиент в примере обращается к 127.0.0.1, однако слушатель принимает на всех интерфейсах. Поэтому этот вариант только lint: не запускается в общей проверочной машине.

Ошибки и следующий шаг. Повторяйте только в своей изолированной VM без внешней сети. Для обычной локальной лаборатории используйте явные create/bind127.0.0.1/listen; полный эквивалент показан в соседних карточках.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Сократить создание IPv4 listener, когда осознанно нужно слушать все местные интерфейсы.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
port0…65535, привязка ко всем IPv4 интерфейсам. 0 выбирает свободный порт, но не ограничивает доступ loopback.
backlogОчередь ожидающих соединений; с PHP 8.4 default SOMAXCONN, раньше 128.
РезультатНовый слушающий Socket/false; accept вызывается отдельно.
Версии/ошибкиPHP 8: объект; PHP 8.5: ValueError для недопустимого порта. Системный отказ — warning + false.
Отказ до создания listener
  1. Передан порт -1
  2. PHP 8.5 проверяет диапазон
  3. ValueError перехвачен
  4. Сетевой listener не создан

Этот вариант. PHP 8.5 проверяет диапазон до открытия listener. Этот самостоятельный пример не создаёт endpoint на всех интерфейсах.

Проверить ошибочный порт короткого конструктораСамостоятельный пример
<?php
declare(strict_types=1);

try {
    socket_create_listen(-1);
} catch (ValueError $error) {
    echo "Listener не создан: порт вне диапазона\n";
}
Ожидаемый результат
Listener не создан: порт вне диапазона

Этот вариант. Контекстный пример: только заранее изолированная учебная машина с согласованной сетевой политикой. В общей проверке выполняется lint, не создание такого listener. Для обычной локальной лаборатории используйте create + bind(127.0.0.1, 0) + listen: create_listen не принимает параметр адреса.

Короткий listener в заранее изолированной средеПример для указанного окружения
<?php
declare(strict_types=1);

$listener = socket_create_listen(0, SOMAXCONN);
if ($listener === false) throw new RuntimeException(socket_strerror(socket_last_error()));
try {
    if (!socket_getsockname($listener, $address, $port)) throw new RuntimeException('Нет адреса');
    echo $address === '0.0.0.0' ? "Слушаются все IPv4 интерфейсы\n" : "Проверьте привязку\n";
} finally { socket_close($listener); }
Ожидаемый результат
Слушаются все IPv4 интерфейсы

Связанные действия: socket_create, socket_bind, socket_listen.

socket_closeЗавершить владение системным endpoint и освободить его после запроса/обмена.

PHP 4.1.0+ · sockets

socket_close(Socket $socket): void

Освободить соединение, сохранив уже полученный товар. Закрывается конечная точка Socket. Уже полученная строка JSON остаётся обычной PHP-строкой и доступна после close.

socket_close($client)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Освободить соединение, сохранив уже полученный товарСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
writeBytes($client, "product:1\n");
serveProduct($peer);
$json = readCatalogLine($client);
socket_close($peer);
socket_close($client);
socket_close($listener);
showProduct($json);
Ожидаемый результат
Молоток — 8 шт.

Сначала получен полный ответ, затем закрыты peer/client/listener. Данные и системный канал имеют разные сроки жизни.

Ошибки и следующий шаг. Повторное использование закрытого объекта вызывает Error. При исключениях назначайте одного владельца и закрывайте ресурсы в finally; присваивание Socket другой переменной не создаёт независимую конечную точку.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Завершить владение системным endpoint и освободить его после запроса/обмена.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый Socket; PHP 8 принимает объект.
Результатvoid; закрытый объект остаётся объектом, но использовать его в операциях нельзя.
Ошибка использованияПовторная операция с закрытым объектом может дать Error. Копия переменной не является копией endpoint.
Закрытие созданного UDP endpoint
  1. create выделяет endpoint
  2. close освобождает его
  3. Возврат null соответствует void
  4. Повторные операции больше недопустимы

Этот вариант. void означает отсутствие полезного возвращаемого значения, а не bool успеха. Переменная всё ещё может ссылаться на объект Socket, но его системный endpoint уже закрыт.

Освободить endpoint после работыСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет endpoint');
$result = socket_close($socket);
echo $result === null ? "Endpoint закрыт; возвращаемого результата нет\n" : "Ошибка\n";
Ожидаемый результат
Endpoint закрыт; возвращаемого результата нет

Этот вариант. Присваивание объекта не дублирует системный сокет. После close не вызывайте close второй раз и не проверяйте пригодность через instanceof: класс объекта от закрытия не меняется.

Не использовать вторую переменную как независимый сокетСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет endpoint');
$anotherName = $socket;
socket_close($socket);
try { socket_get_option($anotherName, SOL_SOCKET, SO_TYPE); }
catch (Error $error) { echo "Обе переменные ссылались на закрытый endpoint\n"; }
Ожидаемый результат
Обе переменные ссылались на закрытый endpoint

Ресурс обычно освобождается и при уничтожении последней ссылки, но явный finally делает владение понятным. SO_LINGER может влиять на завершение и отправку буферов; close не доказывает, что прикладной сервер обработал данные. Для обмена «я закончил отправлять, но ещё жду ответ» сначала нужен shutdown записи, а не полный close.

Связанные действия: socket_shutdown, Socket.

socket_shutdownОстановить одну или обе стороны обмена, сохранив endpoint до последующего close.

PHP 4.1.0+ · sockets

socket_shutdown(Socket $socket, int $mode = 2): bool

Закончить отправку, сохранив возможность получить ответ. Режим 1 завершает направление записи. Клиент больше не посылает команды, но может дочитать ответ сервера. Это половинное завершение, а не close.

socket_shutdown($client, 1)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Закончить отправку, сохранив возможность получить ответСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
writeBytes($client, "product:1\n");
if (!socket_shutdown($client, 1)) { throw new RuntimeException('Запись не завершена'); }
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сервер получает уже отправленную строку; обратное направление остаётся доступным. Нельзя путать EOF одной стороны с отменой данных, которые уже принял другой конец.

Ошибки и следующий шаг. Режим 0 выключает приём,2 — оба направления. Протокол должен объяснять, когда сторона закончила сообщение; shutdown не заменяет разбор HTTP/JSON и не гарантирует прикладную обработку.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Остановить одну или обе стороны обмена, сохранив endpoint до последующего close.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketПодходящий соединённый Socket.
mode0 — чтение, 1 — запись, 2 — оба; PHP 8.5 предоставляет SHUT_RD/SHUT_WR/SHUT_RDWR.
Результатtrue/false. Буферы могут обрабатываться по правилам ОС/протокола; это не прикладное подтверждение доставки.
ОшибкаНеверный режим ValueError; системный отказ warning + false.
Запрос до EOF
  1. Клиент пишет A42
  2. shutdown записи сообщает конец запроса
  3. Сервис читает A42, затем EOF
  4. Сервис отвечает 7
  5. Клиент всё ещё может читать

Этот вариант. mode=1 закрывает отправку клиента, оставляя его приём. После уже посланных байтов peer видит EOF потока. Это пример собственного протокола «запрос до EOF», не правило HTTP persistent connection. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Сообщить конец запроса и продолжить читать ответСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$client, $worker] = labPair();
try {
    labWriteAll($client, 'A42');
    if (!socket_shutdown($client, 1)) throw new RuntimeException('Не удалось закончить отправку');
    echo 'Запрос: ', labReadExact($worker, 3), "\n";
    echo socket_read($worker, 1) === '' ? "Получен конец ввода\n" : "Нет EOF\n";
    labWriteAll($worker, '7');
    echo 'Остаток: ', labReadExact($client, 1), "\n";
} finally { labClose($client, $worker); }
Ожидаемый результат
Запрос: A42
Получен конец ввода
Остаток: 7

Этот вариант. SHUT_RD/WR/RDWR добавлены как PHP-константы в 8.5 и соответствуют режимам 0/1/2. Shutdown не заменяет освобождение объекта через close и не гарантирует, что все буферы приложения уже обработаны. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Проверить режим и использовать имена PHP 8.5Самостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$left, $right] = labPair();
try {
    try { socket_shutdown($left, 9); }
    catch (ValueError $error) { echo "Недопустимый режим отклонён\n"; }
    if (!socket_shutdown($left, SHUT_RDWR)) throw new RuntimeException('Shutdown не выполнен');
    echo "Оба направления остановлены; close ещё нужен\n";
} finally { labClose($left, $right); }
Ожидаемый результат
Недопустимый режим отклонён
Оба направления остановлены; close ещё нужен

Связанные действия: socket_close, socket_read, socket_write.

socket_getsocknameУзнать адрес местной стороны для диагностики и сопоставления соединений.

PHP 4.1.0+ · sockets

socket_getsockname(Socket $socket, string &$address, int &$port = null): bool

Проверить, что клиент связан с собственным слушателем. getsockname описывает локальную сторону объекта, getpeername — удалённую. Выходные host/port заполняются по ссылке и относятся к выбранной конечной точке.

socket_getsockname($listener, $host, $port)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Проверить, что клиент связан с собственным слушателемСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
socket_getsockname($listener, $localHost, $localPort);
socket_getpeername($client, $remoteHost, $remotePort);
if ($localPort !== $remotePort || $remoteHost !== '127.0.0.1') { throw new RuntimeException('Клиент связан с другим узлом'); }
echo 'Сервер клиента: ', $remoteHost, "\n";
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Сервер клиента: 127.0.0.1
Молоток — 8 шт.

ОС выбирает порт, поэтому его не выдумывают постоянным числом. Сравнение связывает два представления одного соединения; следующее чтение подтверждает полезный обмен.

Ошибки и следующий шаг. У UDP getpeername требует выбранного peer. Адрес proxy/NAT может быть не исходным адресом посетителя; эти функции не читают HTTP-заголовки X-Forwarded-For.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Узнать адрес местной стороны для диагностики и сопоставления соединений.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый endpoint; для peer должна существовать осмысленная удалённая сторона.
addressВыходная переменная: IPv4/IPv6 адрес или путь Unix socket.
portНеобязательная выходная переменная с IP-портом; не входной фильтр.
Результатtrue/false; ошибка может не менять last_error для неподдержанного семейства.
Адрес TCP клиента
  1. Есть собственное локальное соединение
  2. socket_getsockname заполняет $address/$port
  3. Адрес 127.0.0.1
  4. Порт проверяется как назначенный, не как заранее известный

Этот вариант. address и port — выходные переменные по ссылке. Функция возвращает bool, а не строку адреса. Числа ephemeral ports намеренно не зафиксированы. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Прочитать местной сторону TCP-соединенияСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$listener, $client, $peer] = labTcp();
try {
    if (!socket_getsockname($client, $address, $port)) throw new RuntimeException('Нет адреса');
    echo $address, "\n", $port > 0 ? "IP-порт назначен\n" : "Нет порта\n";
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
127.0.0.1
IP-порт назначен

Этот вариант. Слова local/peer относительны к объекту: адрес клиента местный для клиента и удалённый для принятого сервером endpoint. За proxy это адрес непосредственного соседа, не обязательно исходного пользователя. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Сопоставить две стороны одного соединенияСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$listener, $client, $peer] = labTcp();
try {
    socket_getsockname($client, $localIp, $localPort);
    socket_getpeername($peer, $remoteIp, $remotePort);
    echo $localIp === $remoteIp && $localPort === $remotePort
        ? "Местный адрес клиента совпадает с peer сервера\n" : "Адреса различаются\n";
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
Местный адрес клиента совпадает с peer сервера

Для AF_UNIX возвращается путь, port не имеет обычного IP-смысла. У безымянного конца пары/непривязанного клиента имя может отсутствовать; не требуйте непустой путь для любого IPC. Для getsockname PHP 8.5 также описывает AF_PACKET: индекс интерфейса и строковое представление. Неизвестное семейство может дать false, не обновив last_error, поэтому не приписывайте старый код ошибки новой операции.

Связанные действия: socket_getpeername, socket_bind.

socket_getpeernameУзнать адрес удалённой стороны для диагностики и сопоставления соединений.

PHP 4.1.0+ · sockets

socket_getpeername(Socket $socket, string &$address, int &$port = null): bool

Проверить, что клиент связан с собственным слушателем. getsockname описывает локальную сторону объекта, getpeername — удалённую. Выходные host/port заполняются по ссылке и относятся к выбранной конечной точке.

socket_getpeername($client, $host, $port)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Проверить, что клиент связан с собственным слушателемСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
socket_getsockname($listener, $localHost, $localPort);
socket_getpeername($client, $remoteHost, $remotePort);
if ($localPort !== $remotePort || $remoteHost !== '127.0.0.1') { throw new RuntimeException('Клиент связан с другим узлом'); }
echo 'Сервер клиента: ', $remoteHost, "\n";
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Сервер клиента: 127.0.0.1
Молоток — 8 шт.

ОС выбирает порт, поэтому его не выдумывают постоянным числом. Сравнение связывает два представления одного соединения; следующее чтение подтверждает полезный обмен.

Ошибки и следующий шаг. У UDP getpeername требует выбранного peer. Адрес proxy/NAT может быть не исходным адресом посетителя; эти функции не читают HTTP-заголовки X-Forwarded-For.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Узнать адрес удалённой стороны для диагностики и сопоставления соединений.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый endpoint; для peer должна существовать осмысленная удалённая сторона.
addressВыходная переменная: IPv4/IPv6 адрес или путь Unix socket.
portНеобязательная выходная переменная с IP-портом; не входной фильтр.
Результатtrue/false; ошибка может не менять last_error для неподдержанного семейства.
Адрес TCP клиента
  1. Есть собственное локальное соединение
  2. socket_getpeername заполняет $address/$port
  3. Адрес 127.0.0.1
  4. Порт проверяется как назначенный, не как заранее известный

Этот вариант. address и port — выходные переменные по ссылке. Функция возвращает bool, а не строку адреса. Числа ephemeral ports намеренно не зафиксированы. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Прочитать удалённой сторону TCP-соединенияСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$listener, $client, $peer] = labTcp();
try {
    if (!socket_getpeername($client, $address, $port)) throw new RuntimeException('Нет адреса');
    echo $address, "\n", $port > 0 ? "IP-порт назначен\n" : "Нет порта\n";
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
127.0.0.1
IP-порт назначен

Этот вариант. Слова local/peer относительны к объекту: адрес клиента местный для клиента и удалённый для принятого сервером endpoint. За proxy это адрес непосредственного соседа, не обязательно исходного пользователя. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Сопоставить две стороны одного соединенияСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$listener, $client, $peer] = labTcp();
try {
    socket_getsockname($client, $localIp, $localPort);
    socket_getpeername($peer, $remoteIp, $remotePort);
    echo $localIp === $remoteIp && $localPort === $remotePort
        ? "Местный адрес клиента совпадает с peer сервера\n" : "Адреса различаются\n";
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
Местный адрес клиента совпадает с peer сервера

Для AF_UNIX возвращается путь, port не имеет обычного IP-смысла. У безымянного конца пары/непривязанного клиента имя может отсутствовать; не требуйте непустой путь для любого IPC. Для getsockname PHP 8.5 также описывает AF_PACKET: индекс интерфейса и строковое представление. Неизвестное семейство может дать false, не обновив last_error, поэтому не приписывайте старый код ошибки новой операции.

Связанные действия: socket_getsockname, socket_bind.

socket_readПолучить очередную часть входящих байтов либо прочитать до строкового разделителя.

PHP 4.1.0+ · sockets

socket_read(Socket $socket, int $length, int $mode = PHP_BINARY_READ): string|false

Передать команду товара и проверить фактически прочитанные байты. Запись и чтение оперируют байтами. socket_write/read возвращают число или строку; send/recv принимают явные флаги, а recv заполняет строковую переменную по ссылке.

socket_read(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать команду товара и проверить фактически прочитанные байтыСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
$request = "product:1\n";
$sent = socket_write($client, $request);
if ($sent !== strlen($request)) { throw new RuntimeException('Короткий запрос не записан полностью'); }
$received = '';
while (strlen($received) < strlen($request)) {
    $part = socket_read($peer, strlen($request) - strlen($received));
    if ($part === false || $part === '') { throw new RuntimeException('Запрос оборвался'); }
    $received .= $part;
}
echo 'Сервер получил: ', trim($received), "\n";
$catalog = ["product:1\n"=>['name'=>'Молоток','stock'=>8]];
$row = $catalog[$received] ?? ['error'=>'Команда неизвестна'];
writeBytes($peer, json_encode($row, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)."\n");
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Сервер получил: product:1
Молоток — 8 шт.

Сначала видна точная команда, полученная сервером. Затем он выбирает по ней известную строку и отправляет JSON. Чтение TCP не обязано совпасть с границей отдельной записи.

Ошибки и следующий шаг. Для большого/неблокирующего потока нужны циклы частичных операций и общий deadline. В этом минимуме короткая запись проверяется целиком; полный writeBytes в приложении показывает цикл. MSG_WAITALL не гарантирует успех при EOF/сигнале/ошибке, а длина UTF-8 измеряется strlen.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Получить очередную часть входящих байтов либо прочитать до строкового разделителя.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketСоединённый endpoint; режим блокировки/таймаут действует на чтение.
lengthМаксимальное число байтов; выбирайте положительный ограниченный размер.
modePHP_BINARY_READ (default) сохраняет любые байты; PHP_NORMAL_READ останавливается на CR/LF.
РезультатСтрока данных, пустая строка EOF или false при ошибке. "0" — данные, не отказ.
ОшибкиWarning/false с кодом socket_last_error; неверный тип/закрытый Socket дают PHP-ошибку.
Команда status
  1. Отправитель пишет status и LF
  2. NORMAL_READ читает до LF
  3. rtrim убирает только CR/LF
  4. Команда выводится как status

Этот вариант. PHP_NORMAL_READ останавливается на или либо пределе длины; завершающий байт входит в результат. rtrim здесь удаляет только окончания строк. Для CRLF отдельный может закончить один вызов, оставив следующему; полноценный line parser должен учитывать это. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Прочитать строковую команду, включая её конецСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    labWriteAll($sender, "status\n");
    $line = socket_read($receiver, 64, PHP_NORMAL_READ);
    if ($line === false) throw new RuntimeException(socket_strerror(socket_last_error($receiver)));
    echo 'Команда: ', rtrim($line, "\r\n"), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Команда: status

Этот вариант. Бинарный режим не считает NUL концом строки: это обычный байт. Цикл собирает известные три байта и не предполагает, что один write соответствует одному read. bin2hex делает невидимый байт видимым. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Прочитать бинарное значение с нулевым байтомСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    labWriteAll($sender, "A\0B");
    $data = '';
    while (strlen($data) < 3) {
        $chunk = socket_read($receiver, 3 - strlen($data), PHP_BINARY_READ);
        if ($chunk === false || $chunk === '') throw new RuntimeException('Неполное значение');
        $data .= $chunk;
    }
    echo bin2hex($data), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
410042

length — максимум, а не обещание получить столько. В бинарном режиме пустая строка означает EOF потока; false — отказ, в том числе временный EWOULDBLOCK в non-blocking режиме. Разрыв с ошибкой и нормальное закрытие различаются. На странице PHP.net рядом встречаются формулировки о false при закрытии и о пустой строке: в рабочем коде обработайте обе ситуации, но не считайте EOF транспортной ошибкой автоматически. Для нормального режима также учитывайте его построчное поведение; для собственных протоколов обычно проще бинарный буфер.

Связанные действия: socket_recv, socket_write, socket_select.

socket_recvПолучить данные с управляющими флагами и отдельным числом принятых байтов.

PHP 4.1.0+ · sockets

socket_recv(Socket $socket, ?string &$data, int $length, int $flags): int|false

Передать команду товара и проверить фактически прочитанные байты. Запись и чтение оперируют байтами. socket_write/read возвращают число или строку; send/recv принимают явные флаги, а recv заполняет строковую переменную по ссылке.

socket_recv(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать команду товара и проверить фактически прочитанные байтыСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
$request = "product:1\n";
$sent = socket_write($client, $request);
if ($sent !== strlen($request)) { throw new RuntimeException('Короткий запрос не записан полностью'); }
$size = socket_recv($peer, $received, strlen($request), MSG_WAITALL);
if ($size !== strlen($request)) { throw new RuntimeException('Запрос получен не целиком'); }
echo 'Сервер получил: ', trim($received), "\n";
$catalog = ["product:1\n"=>['name'=>'Молоток','stock'=>8]];
$row = $catalog[$received] ?? ['error'=>'Команда неизвестна'];
writeBytes($peer, json_encode($row, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)."\n");
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Сервер получил: product:1
Молоток — 8 шт.

Сначала видна точная команда, полученная сервером. Затем он выбирает по ней известную строку и отправляет JSON. Чтение TCP не обязано совпасть с границей отдельной записи.

Ошибки и следующий шаг. Для большого/неблокирующего потока нужны циклы частичных операций и общий deadline. В этом минимуме короткая запись проверяется целиком; полный writeBytes в приложении показывает цикл. MSG_WAITALL не гарантирует успех при EOF/сигнале/ошибке, а длина UTF-8 измеряется strlen.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Получить данные с управляющими флагами и отдельным числом принятых байтов.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketСоединённый endpoint.
dataВыходная переменная по ссылке: строка при положительном чтении, null при отсутствии данных/ошибке.
lengthМаксимум байтов. Не число символов и не гарантированная длина.
flags0 либо поддерживаемые MSG_* биты; доступность зависит от ОС/протокола.
РезультатЧисло байтов (включая 0) или строго false. Сначала различайте эти значения, затем используйте data.
Просмотр без удаления
  1. В очередь приходит A42
  2. PEEK длиной 1 возвращает A
  3. Очередь не изменилась
  4. Обычное чтение получает A42

Этот вариант. MSG_PEEK не удаляет просмотренные байты: обычное следующее чтение всё ещё видит A42. Возвращаемое число и выходная переменная $peek — разные результаты. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Посмотреть первый байт, не забирая его из очередиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    labWriteAll($sender, 'A42');
    $peek = null;
    if (socket_recv($receiver, $peek, 1, MSG_PEEK) !== 1) throw new RuntimeException('Нет первого байта');
    echo 'Первый байт: ', $peek, "\n";
    echo 'Полные данные: ', labReadExact($receiver, 3), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Первый байт: A
Полные данные: A42

Этот вариант. Для TCP 0 означает упорядоченный конец потока, а не false. PHP очищает data в null, когда recv не вернул положительное количество байтов. Для UDP нулевая длина может быть настоящим пустым datagram, поэтому смысл зависит от типа сокета. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отличить ноль полученных байтов от ошибкиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_shutdown($sender, 1);
    $data = 'старое значение';
    $count = socket_recv($receiver, $data, 32, 0);
    echo $count === 0 ? "Конец TCP потока\n" : "Неожиданный результат\n";
    echo $data === null ? "Выходной буфер очищен\n" : "В буфере остались данные\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Конец TCP потока
Выходной буфер очищен

flags=0 — обычное чтение. MSG_OOB читает срочные данные, MSG_PEEK не удаляет байты, MSG_WAITALL старается дождаться length, но сигнал/EOF/отказ может вернуть меньше. MSG_DONTWAIT делает один вызов неблокирующим там, где этот флаг доступен. Не используйте WAITALL для неизвестной длины ответа: можно ждать данных, которые по протоколу никогда не придут. Флаги объединяются | только при совместимом назначении.

Связанные действия: socket_read, socket_recvfrom, socket_recvmsg.

socket_writeПередать байты в соединённый поток и узнать, сколько из них принято системой.

PHP 4.1.0+ · sockets

socket_write(Socket $socket, string $data, ?int $length = null): int|false

Передать команду товара и проверить фактически прочитанные байты. Запись и чтение оперируют байтами. socket_write/read возвращают число или строку; send/recv принимают явные флаги, а recv заполняет строковую переменную по ссылке.

socket_write(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать команду товара и проверить фактически прочитанные байтыСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
$request = "product:1\n";
$sent = socket_write($client, $request);
if ($sent !== strlen($request)) { throw new RuntimeException('Короткий запрос не записан полностью'); }
$received = readCatalogLine($peer);
echo 'Сервер получил: ', trim($received), "\n";
$catalog = ["product:1\n"=>['name'=>'Молоток','stock'=>8]];
$row = $catalog[$received] ?? ['error'=>'Команда неизвестна'];
writeBytes($peer, json_encode($row, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)."\n");
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Сервер получил: product:1
Молоток — 8 шт.

Сначала видна точная команда, полученная сервером. Затем он выбирает по ней известную строку и отправляет JSON. Чтение TCP не обязано совпасть с границей отдельной записи.

Ошибки и следующий шаг. Для большого/неблокирующего потока нужны циклы частичных операций и общий deadline. В этом минимуме короткая запись проверяется целиком; полный writeBytes в приложении показывает цикл. MSG_WAITALL не гарантирует успех при EOF/сигнале/ошибке, а длина UTF-8 измеряется strlen.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Передать байты в соединённый поток и узнать, сколько из них принято системой.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketСоединённый endpoint, обычно TCP/локальная пара.
dataДвоично безопасная строка; strlen измеряет байты.
lengthНеобязательный максимум отправки; null — вся строка. Больше длины строки автоматически ограничивается.
Результат0 или положительное число записанных байтов; false — отказ. Частичный результат допустим.
ОшибкаWarning/false и код у socket; для non-blocking EWOULDBLOCK требует ожидания, а не повтора всей строки.
Нулевая и обычная запись
  1. write A42 с length=0
  2. Возврат 0: ничего не отправлено
  3. Следующая запись передаёт A42
  4. Приёмник читает A42

Этот вариант. Явный length=0 ограничивает первую операцию нулём; она не отправляет A42. Во второй записи используется helper с циклом. Проверка if (!$count) потеряла бы различие 0/false. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Не спутать нулевую запись с ошибкойСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    $count = socket_write($sender, 'A42', 0);
    echo $count === 0 ? "Ноль байтов записано; это не false\n" : "Неожиданный результат\n";
    labWriteAll($sender, 'A42');
    echo labReadExact($receiver, 3), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Ноль байтов записано; это не false
A42

Этот вариант. offset — уже принятые системой байты. Следующий вызов получает только остаток. Здесь малая команда и blocking socket с timeout; в event loop вместо немедленного отказа на EWOULDBLOCK сохраняют offset и ждут writable. Не повторяйте весь payload после частичной записи. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Передать команду полностью с учётом частичных записейСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    $command = "reserve A42 2\n";
    $offset = 0;
    while ($offset < strlen($command)) {
        $count = socket_write($sender, substr($command, $offset));
        if ($count === false || $count === 0) throw new RuntimeException('Не удалось продвинуть запись');
        $offset += $count;
    }
    echo labReadExact($receiver, strlen($command));
} finally { labClose($sender, $receiver); }
Ожидаемый результат
reserve A42 2

Необязательный length=null (PHP 8+) означает длину строки; больше strlen PHP ограничивает фактической длиной. Возвращаемое число описывает принятые системой байты, а не подтверждение бизнес-действия сервером. Большую двустороннюю передачу нельзя бездумно организовать «сначала всё записать, затем всё прочитать»: обе стороны могут заполнить буферы и ждать друг друга.

Связанные действия: socket_send, socket_read, socket_select.

socket_sendОтправить данные в соединённый endpoint с явно выбранными флагами.

PHP 4.1.0+ · sockets

socket_send(Socket $socket, string $data, int $length, int $flags): int|false

Передать команду товара и проверить фактически прочитанные байты. Запись и чтение оперируют байтами. socket_write/read возвращают число или строку; send/recv принимают явные флаги, а recv заполняет строковую переменную по ссылке.

socket_send(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать команду товара и проверить фактически прочитанные байтыСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
$request = "product:1\n";
$sent = socket_send($client, $request, strlen($request), 0);
if ($sent !== strlen($request)) { throw new RuntimeException('Короткий запрос не записан полностью'); }
$received = readCatalogLine($peer);
echo 'Сервер получил: ', trim($received), "\n";
$catalog = ["product:1\n"=>['name'=>'Молоток','stock'=>8]];
$row = $catalog[$received] ?? ['error'=>'Команда неизвестна'];
writeBytes($peer, json_encode($row, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)."\n");
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Сервер получил: product:1
Молоток — 8 шт.

Сначала видна точная команда, полученная сервером. Затем он выбирает по ней известную строку и отправляет JSON. Чтение TCP не обязано совпасть с границей отдельной записи.

Ошибки и следующий шаг. Для большого/неблокирующего потока нужны циклы частичных операций и общий deadline. В этом минимуме короткая запись проверяется целиком; полный writeBytes в приложении показывает цикл. MSG_WAITALL не гарантирует успех при EOF/сигнале/ошибке, а длина UTF-8 измеряется strlen.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Отправить данные в соединённый endpoint с явно выбранными флагами.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketСоединённый TCP endpoint либо UDP с выбранным peer.
data/lengthСтрока и количество первых байтов для отправки. Ограничивайте length фактической длиной; отрицательный недопустим.
flags0 либо совместимые MSG_* биты, доступные в ОС и выбранном протоколе.
РезультатЧисло принятых байтов либо false. Для TCP допустима частичная запись; успех не подтверждает обработку peer.
Отправить A42 из A42-rest
  1. data содержит 8 байтов
  2. length=3 выбирает префикс
  3. send принимает 3 байта
  4. Приёмник получает A42

Этот вариант. length=3 ограничивает отправку первыми тремя байтами; flags=0 — обычная отправка. TCP всё равно может принять меньше, что первый короткий опыт явно проверяет. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Передать только указанный префикс строкиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    $count = socket_send($sender, 'A42-rest', 3, 0);
    if ($count !== 3) throw new RuntimeException('Неполная учебная отправка');
    echo labReadExact($receiver, 3), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
A42

Этот вариант. Функция пригодна для небольшой blocking-команды с настроенными таймаутами. Для UDP не переносите цикл остатков автоматически: повторные вызовы означали бы разные datagrams, а не продолжение одного сообщения. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Использовать send в функции отправки небольшой командыСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
function sendCommand(Socket $socket, string $command): void {
    $offset = 0;
    while ($offset < strlen($command)) {
        $part = substr($command, $offset);
        $count = socket_send($socket, $part, strlen($part), 0);
        if ($count === false || $count === 0) throw new RuntimeException('Команда не отправлена полностью');
        $offset += $count;
    }
}
[$sender, $receiver] = labPair();
try {
    sendCommand($sender, "stock A42\n");
    echo labReadExact($receiver, 10);
} finally { labClose($sender, $receiver); }
Ожидаемый результат
stock A42

MSG_OOB относится к срочным данным поддержанного протокола, MSG_EOR — концу записи, MSG_EOF — специализированному завершению отправки, MSG_DONTROUTE — обходу обычной маршрутизации. Они не универсальны: EOR/EOF отсутствуют на Windows. На Unix MSG_NOSIGNAL может предотвращать SIGPIPE для отдельной отправки; доступность нужно проверить. Бинарный 0-флаг не означает «отключить ошибки».

Связанные действия: socket_write, socket_sendto, socket_sendmsg.

socket_sendtoОтправить отдельное сообщение указанному адресату, например UDP-показание и ответ на него.

PHP 4.1.0+ · sockets

socket_sendto(Socket $socket, string $data, int $length, int $flags, string $address, ?int $port = null): int|false

Получить одну строку инвентаризации отдельной UDP-датаграммой. UDP сохраняет границы датаграммы, но не гарантирует доставку/порядок. sendmsg собирает её из iov-буферов; recvmsg возвращает iov и метаданные массивом; sendto/recvfrom используют явный адрес.

socket_sendto(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Получить одну строку инвентаризации отдельной UDP-датаграммойСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$receiver = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
$sender = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
if (!socket_bind($receiver, '127.0.0.1', 0) || !socket_bind($sender, '127.0.0.1', 0)) { throw new RuntimeException('UDP не привязан'); }
localTimeout($receiver);
localTimeout($sender);
socket_getsockname($receiver, $host, $port);
$payload = "Молоток;8\n";
$sent = socket_sendto($sender, $payload, strlen($payload), 0, '127.0.0.1', $port);
if ($sent !== strlen($payload)) { throw new RuntimeException('Датаграмма не отправлена полностью'); }
$received = socket_recvfrom($receiver, $data, 1024, 0, $from, $fromPort);
if ($received === false || $data !== $payload) { throw new RuntimeException('Датаграмма не получена целиком'); }
echo "Получена строка инвентаризации:\n", $data;
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Получена строка инвентаризации:
Молоток;8

Полезный результат — реально полученные байты строки, не числовой успех отправки. Оба endpoint привязаны к loopback со свободными портами, предел приёма 1024 байта достаточен для известного сообщения.

Ошибки и следующий шаг. Слишком малый буфер может обрезать датаграмму; оставшуюся часть нельзя дочитать как TCP. Системные флаги усечения и доверие к адресу отправителя учитывают отдельно. Для изменения склада одного UDP без подтверждения/идемпотентности недостаточно.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Отправить отдельное сообщение указанному адресату, например UDP-показание и ответ на него.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketПодходящий endpoint; может быть соединённым или несоединённым в зависимости от протокола.
data/lengthБайты одного сообщения и их число; UDP имеет максимальную длину datagram, слишком большая может дать EMSGSIZE.
flagsMSG_* по договору send и поддержке ОС.
address/portУдалённый IP/порт, для Unix — путь; для IPv4/IPv6 порт нужен.
РезультатЧисло отправленных байтов/false. Это не подтверждение доставки UDP.
ВерсииPHP 8 nullable port и Socket; PHP 8.5 ValueError для порта вне 0…65535.
Показание T=21
  1. Receiver привязан к localhost и свободному порту
  2. Sender отправляет 4 байта на этот порт
  3. recvfrom получает одну datagram
  4. На экран выводится T=21

Этот вариант. sendto указывает адрес именно для этой datagram. Наличие локального receiver делает опыт воспроизводимым; обычная успешная UDP-отправка не гарантирует существования получателя или доставки. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отправить показание на собственный UDP endpointСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    $message = 'T=21';
    if (socket_sendto($sender, $message, strlen($message), 0, '127.0.0.1', $port) !== 4) throw new RuntimeException('Неполная отправка');
    $data = '';
    if (socket_recvfrom($receiver, $data, 64, 0, $from, $fromPort) === false) throw new RuntimeException('Нет сообщения');
    echo $data, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
T=21

Этот вариант. Ответ отправляется на $from/$fromPort, полученные recvfrom. Возврат на заранее придуманный клиентский порт часто и вызывает ситуацию «сервер получил, клиент нет». Учебный ACK не решает повторы/потери: реальному протоколу нужны ID сообщения, дедупликация и пределы ожидания. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Ответить на фактический адрес отправителяСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    socket_sendto($sender, 'T=21', 4, 0, '127.0.0.1', $port);
    $data = '';
    if (socket_recvfrom($receiver, $data, 64, 0, $from, $fromPort) === false) throw new RuntimeException('Нет показания');
    if (socket_sendto($receiver, 'ACK', 3, 0, $from, $fromPort) !== 3) throw new RuntimeException('Нет ACK');
    $reply = '';
    if (socket_recvfrom($sender, $reply, 64, 0, $replyFrom, $replyPort) === false) throw new RuntimeException('Нет ответа');
    echo $data, ' -> ', $reply, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
T=21 -> ACK

Связанные действия: socket_recvfrom, socket_send, socket_bind.

socket_recvfromПринять одно сообщение и узнать, куда при необходимости отправить ответ.

PHP 4.1.0+ · sockets

socket_recvfrom(Socket $socket, string &$data, int $length, int $flags, string &$address, int &$port = null): int|false

Получить одну строку инвентаризации отдельной UDP-датаграммой. UDP сохраняет границы датаграммы, но не гарантирует доставку/порядок. sendmsg собирает её из iov-буферов; recvmsg возвращает iov и метаданные массивом; sendto/recvfrom используют явный адрес.

socket_recvfrom(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Получить одну строку инвентаризации отдельной UDP-датаграммойСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$receiver = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
$sender = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
if (!socket_bind($receiver, '127.0.0.1', 0) || !socket_bind($sender, '127.0.0.1', 0)) { throw new RuntimeException('UDP не привязан'); }
localTimeout($receiver);
localTimeout($sender);
socket_getsockname($receiver, $host, $port);
$payload = "Молоток;8\n";
$sent = socket_sendto($sender, $payload, strlen($payload), 0, '127.0.0.1', $port);
if ($sent !== strlen($payload)) { throw new RuntimeException('Датаграмма не отправлена полностью'); }
$received = socket_recvfrom($receiver, $data, 1024, 0, $from, $fromPort);
if ($received === false || $data !== $payload) { throw new RuntimeException('Датаграмма не получена целиком'); }
echo "Получена строка инвентаризации:\n", $data;
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Получена строка инвентаризации:
Молоток;8

Полезный результат — реально полученные байты строки, не числовой успех отправки. Оба endpoint привязаны к loopback со свободными портами, предел приёма 1024 байта достаточен для известного сообщения.

Ошибки и следующий шаг. Слишком малый буфер может обрезать датаграмму; оставшуюся часть нельзя дочитать как TCP. Системные флаги усечения и доверие к адресу отправителя учитывают отдельно. Для изменения склада одного UDP без подтверждения/идемпотентности недостаточно.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Принять одно сообщение и узнать, куда при необходимости отправить ответ.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketDatagram endpoint либо иной подходящий соединённый/несоединённый сокет.
dataВыходная строка байтов.
length/flagsМаксимум принимаемых байтов и MSG_* флаги; размер не является гарантией полного сообщения.
address/portВыходной адрес отправителя/порт для unconnected IP; Unix возвращает путь. Не входной фильтр.
РезультатЧисло байтов, включая допустимый 0 для пустого UDP, либо false.
Остаток товара A42
  1. Datagram A42=7 приходит на receiver
  2. recvfrom получает байты и адрес отправителя
  3. Возврат сообщает 5 байтов
  4. Приложение видит localhost и обратный порт

Этот вариант. Параметры data/address/port — выходы, а не заранее заданные фильтры. Адрес/порт получены от ОС; доверие прикладным данным они сами не обеспечивают. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Прочитать сообщение вместе с обратным адресомСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    socket_sendto($sender, 'A42=7', 5, 0, '127.0.0.1', $port);
    $data = '';
    $count = socket_recvfrom($receiver, $data, 64, 0, $from, $fromPort);
    if ($count === false) throw new RuntimeException('Нет datagram');
    echo $data, ' от ', $from, "\n";
    echo $fromPort > 0 ? "Есть обратный порт\n" : "Нет порта\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
A42=7 от 127.0.0.1
Есть обратный порт

Этот вариант. В UDP ноль байтов не является TCP EOF. При недостаточном буфере хвост datagram обычно теряется (поведение ошибок/MSG_TRUNC зависит от ОС), поэтому максимальный размер сообщения нужно определить в протоколе. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Принять допустимое UDP-сообщение нулевой длиныСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    if (socket_sendto($sender, '', 0, 0, '127.0.0.1', $port) === false) throw new RuntimeException('Не отправлено');
    $data = '';
    $count = socket_recvfrom($receiver, $data, 64, 0, $from, $fromPort);
    echo $count === 0 ? "Получена пустая datagram\n" : "Неожиданный результат\n";
    echo $fromPort > 0 ? "Отправитель всё равно известен\n" : "Нет адреса\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Получена пустая datagram
Отправитель всё равно известен

Режимы MSG_PEEK/WAITALL/DONTWAIT/OOB применимы только по правилам конкретного протокола и ОС. WAITALL не склеивает UDP-пакеты в надёжный поток. Для connected socket адресные выходы могут не иметь того же смысла/значения, что для unconnected UDP; документация допускает null. Если нужен устойчивый peer соединения, используйте getpeername. При отказе не обрабатывайте старое содержимое выходных переменных как новое сообщение.

Связанные действия: socket_sendto, socket_recv, socket_recvmsg.

socket_selectЖдать готовности группы endpoint без постоянного опроса и без блокировки на одном клиенте.

PHP 4.1.0+ · sockets

socket_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, int $microseconds = 0): int|false

Дождаться ответа без пустого бесконечного опроса. select изменяет переданные массивы, оставляя готовые объекты. set_nonblock/set_block задают, будет ли сама операция I/O ожидать данные; они не запускают второй PHP-процесс.

socket_select(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Дождаться ответа без пустого бесконечного опросаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
writeBytes($client, "product:1\n");
serveProduct($peer);
$read = [$client];
$write = null;
$except = null;
$ready = socket_select($read, $write, $except, 1);
if ($ready === false || $ready === 0) { throw new RuntimeException('Ответ не готов'); }
showProduct(readCatalogLine($read[0]));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сервер уже записал короткий ответ. select подтверждает готовность выбранного клиента, затем программа получает JSON и показывает товар.

Ошибки и следующий шаг. Готовность не означает, что произвольное длинное сообщение получено целиком. Неблокирующий парсер сохраняет частичный буфер и снова ждёт готовности; наш малый ответ полностью записан раньше. Массивы для следующего select собирают заново.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Ждать готовности группы endpoint без постоянного опроса и без блокировки на одном клиенте.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
read/write/exceptМассивы Socket по ссылке; после вызова только готовые элементы с прежними ключами.
seconds/microsecondsПредел ожидания; null seconds — без предела, 0/0 — poll. Передавайте корректные неотрицательные значения.
РезультатЧисло готовых элементов в результирующих наборах, 0 — timeout, false — системный отказ.
ОшибкиПроверять строго === false; общая ошибка через socket_last_error() без объекта.
Готовый остаток stock
  1. В receiver уже отправлен байт 7
  2. read хранит stock → receiver
  3. select оставляет готовый stock
  4. Чтение получает 7

Этот вариант. select заменяет массивы только готовыми элементами, сохраняя ключи. На каждом новом обороте готовьте копии полного набора заново. null нужно сначала положить в переменную: аргументы передаются по ссылке. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Дождаться читаемой пары, сохранив её имя в массивеСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    labWriteAll($sender, '7');
    $read = ['stock' => $receiver]; $write = null; $except = null;
    $ready = socket_select($read, $write, $except, 1, 0);
    if ($ready === false) throw new RuntimeException(socket_strerror(socket_last_error()));
    if ($ready === 0) throw new RuntimeException('Истёк срок ожидания');
    echo array_key_exists('stock', $read) ? "Ключ stock сохранён\n" : "Ключ потерян\n";
    echo labReadExact($read['stock'], 1), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Ключ stock сохранён
7

Этот вариант. select сообщает, что операция может завершиться без ожидания; он не обещает непустой payload. Сокет на EOF надо убрать из обслуживаемого набора и закрыть по своей политике, иначе цикл будет немедленно просыпаться снова. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Готовность к чтению может означать EOFСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_shutdown($sender, 1);
    $read = [$receiver]; $write = null; $except = null;
    $ready = socket_select($read, $write, $except, 1);
    if ($ready === false || $ready === 0) throw new RuntimeException('Нет готовности');
    echo socket_read($receiver, 16) === '' ? "Чтение готово: достигнут EOF\n" : "Получены данные\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Чтение готово: достигнут EOF

seconds + microseconds задают предел ожидания; null у seconds допускает бесконечное ожидание, 0/0 — только текущая проверка. Не превращайте нулевой poll в горячий цикл. Когда у сервиса нет других дел, ожидание события удобно; когда есть срок задачи/остановка, необходим конечный deadline. Часы и планировщик не обещают точного момента пробуждения. Хотя старая Notes рекомендует ждать без timeout, это не универсальная политика для web/учебного клиента.

Все три аргумента-массива обязательны, ненужные могут быть null/пустыми, хотя бы один должен содержать сокеты. write включают только при наличии ожидающих отправки данных, иначе готовый канал создаст busy loop. except обычно относится к OOB, а не ко всем возможным ошибкам. Проверяйте каждый возвращённый готовый элемент. Готовность не гарантирует завершение большого read/write целиком; для event loop используйте non-blocking и сохраняйте буферы/offset. Системный select имеет ограничения числа дескрипторов и не подходит для любой огромной нагрузки.

Связанные действия: socket_set_nonblock, socket_read, socket_write.

socket_set_nonblockСделать операции быстрыми попытками, чтобы программа могла обслуживать несколько endpoint.

PHP 4.1.0+ · sockets

socket_set_nonblock(Socket $socket): bool

Дождаться ответа без пустого бесконечного опроса. select изменяет переданные массивы, оставляя готовые объекты. set_nonblock/set_block задают, будет ли сама операция I/O ожидать данные; они не запускают второй PHP-процесс.

socket_set_nonblock(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Дождаться ответа без пустого бесконечного опросаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
if (!socket_set_nonblock($client)) { throw new RuntimeException('Неблокирующий режим недоступен'); }
writeBytes($client, "product:1\n");
serveProduct($peer);
$read = [$client];
$write = null;
$except = null;
$ready = socket_select($read, $write, $except, 1);
if ($ready === false || $ready === 0) { throw new RuntimeException('Ответ не готов'); }
showProduct(readCatalogLine($read[0]));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сервер уже записал короткий ответ. select подтверждает готовность выбранного клиента, затем программа получает JSON и показывает товар.

Ошибки и следующий шаг. Готовность не означает, что произвольное длинное сообщение получено целиком. Неблокирующий парсер сохраняет частичный буфер и снова ждёт готовности; наш малый ответ полностью записан раньше. Массивы для следующего select собирают заново.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Сделать операции быстрыми попытками, чтобы программа могла обслуживать несколько endpoint.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый endpoint. Режим относится к его операциям read/write/connect/accept.
Результатtrue/false. При отсутствии готовности последующие операции возвращают временный отказ, вместо ожидания.
ГраницаЭто не новый поток выполнения и не автоматический event loop; дальнейшее ожидание организует приложение.
Данных пока нет
  1. Создана пустая соединённая пара
  2. Receiver переведён в non-blocking
  3. read возвращает false + would-block
  4. Программа может перейти к другим действиям

Этот вариант. Non-blocking не означает, что данные появятся сами. Ожидаемое предупреждение одного вызова подавлено через @, а код немедленно проверен. Реальный отказ соединения нельзя принимать за временное отсутствие данных. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Вернуть управление, когда данных ещё нетСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_set_nonblock($receiver);
    $data = @socket_read($receiver, 16);
    $error = socket_last_error($receiver);
    $pending = $error === SOCKET_EWOULDBLOCK || (defined('SOCKET_EAGAIN') && $error === constant('SOCKET_EAGAIN'));
    echo $data === false && $pending ? "Пока нет данных; управление возвращено\n" : "Другой результат\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Пока нет данных; управление возвращено

Этот вариант. select ограничивает ожидание; non-blocking защищает саму последующую операцию от неожиданного ожидания при изменении состояния. Для нескольких клиентов сохраняют отдельные буферы и обрабатывают их готовность в одном цикле. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Ждать событие перед следующей попыткой чтенияСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_set_nonblock($receiver);
    labWriteAll($sender, '7');
    $read = [$receiver]; $write = null; $except = null;
    $ready = socket_select($read, $write, $except, 1);
    if ($ready === false || $ready === 0) throw new RuntimeException('Нет готовности');
    $data = socket_read($receiver, 1);
    if ($data === false || $data === '') throw new RuntimeException('Нет байта');
    echo 'Остаток: ', $data, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Остаток: 7

Связанные действия: socket_set_block, socket_select.

socket_set_blockВернуть блокирующее поведение endpoint, когда один последовательный обмен проще event loop.

PHP 4.2.0+ · sockets

socket_set_block(Socket $socket): bool

Дождаться ответа без пустого бесконечного опроса. select изменяет переданные массивы, оставляя готовые объекты. set_nonblock/set_block задают, будет ли сама операция I/O ожидать данные; они не запускают второй PHP-процесс.

socket_set_block(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Дождаться ответа без пустого бесконечного опросаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
socket_set_nonblock($client);
if (!socket_set_block($client)) { throw new RuntimeException('Блокирующий режим не восстановлен'); }
writeBytes($client, "product:1\n");
serveProduct($peer);
$read = [$client];
$write = null;
$except = null;
$ready = socket_select($read, $write, $except, 1);
if ($ready === false || $ready === 0) { throw new RuntimeException('Ответ не готов'); }
showProduct(readCatalogLine($read[0]));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сервер уже записал короткий ответ. select подтверждает готовность выбранного клиента, затем программа получает JSON и показывает товар.

Ошибки и следующий шаг. Готовность не означает, что произвольное длинное сообщение получено целиком. Неблокирующий парсер сохраняет частичный буфер и снова ждёт готовности; наш малый ответ полностью записан раньше. Массивы для следующего select собирают заново.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Вернуть блокирующее поведение endpoint, когда один последовательный обмен проще event loop.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый Socket.
Результатtrue/false; снимает non-blocking режим.
ПоведениеRead/send/connect/accept могут ждать готовности/сигнала/ошибки. Таймаут и срок всей задачи настраивают отдельно.
Возврат blocking режима
  1. Receiver был non-blocking
  2. set_block восстанавливает ожидание
  3. Другой конец пишет ready
  4. Чтение получает 5 байтов

Этот вариант. Blocking возвращает ожидание внутри операции. Учебная пара уже имеет таймаут; в примере данные отправлены до чтения, поэтому ждать внешний сервис не требуется. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Вернуть обычное ожидание после настройкиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_set_nonblock($receiver);
    if (!socket_set_block($receiver)) throw new RuntimeException('Режим не восстановлен');
    labWriteAll($sender, 'ready');
    echo labReadExact($receiver, 5), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
ready

Этот вариант. Режим ожидания и его ограничение — разные настройки. SO_RCVTIMEO относится к поддержанным ОС операциям ввода, не является общим deadline всего приложения и не заменяет таймаут select. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Настроить ограниченное ожидание для одного клиентаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
$socket = labSocket(AF_INET, SOCK_STREAM, SOL_TCP);
try {
    if (!socket_set_block($socket)) throw new RuntimeException('Нет blocking режима');
    if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 1, 'usec' => 0])) throw new RuntimeException('Нет таймаута');
    echo "Blocking чтение ограничено таймаутом ввода\n";
} finally { socket_close($socket); }
Ожидаемый результат
Blocking чтение ограничено таймаутом ввода

Связанные действия: socket_set_nonblock, socket_set_option, socket_select.

socket_last_errorУзнать причину отказа после проверки результата операции.

PHP 4.1.0+ · sockets

socket_last_error(?Socket $socket = null): int

Отличить отсутствие данных сейчас от потерянного ответа. После неблокирующего чтения без данных системный код объясняет would-block. last_error читает его, strerror даёт описание ОС, clear_error убирает сохранённый код, но ничего не чинит в соединении.

socket_last_error($client)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Новая конструкция именно здесьКак она участвует в обмене
set_error_handler(function ...)PHP вызывает этот обработчик при предупреждении внутри ограниченного опыта. Возврат true помечает предупреждение обработанным; restore_error_handler в finally возвращает прежнее поведение. Это не callback чтения сокета.
Отличить отсутствие данных сейчас от потерянного ответаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
socket_set_nonblock($client);
set_error_handler(function (int $level): bool { return $level === E_WARNING; });
try { $empty = socket_read($client, 1); } finally { restore_error_handler(); }
$code = socket_last_error($client);
if ($empty !== false || $code === 0) { throw new RuntimeException('Ожидалось отсутствие данных сейчас'); }
$diagnostic = ['code'=>$code, 'message'=>socket_strerror($code)];
if (file_put_contents(__DIR__ . '/socket-error.json', json_encode($diagnostic, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)) === false) { throw new RuntimeException('Диагностика не записана'); }
socket_clear_error($client);
socket_set_block($client);
echo "Ответ ещё не поступил; канал остаётся открыт\n";
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Ответ ещё не поступил; канал остаётся открыт
Молоток — 8 шт.

Ожидаемое предупреждение перехвачено только вокруг одной демонстрационной операции. socket-error.json сохраняет фактические code/message вашей ОС; затем сервер получает запрос и отвечает нормально.

Ошибки и следующий шаг. В приложении классифицируйте EAGAIN/EWOULDBLOCK и другие ошибки по доступным платформенным константам. Не подавляйте все ошибки вокруг всего цикла. Старый код мог сохраниться после успешной операции, поэтому явная очистка полезна перед диагностикой.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Узнать причину отказа после проверки результата операции.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketКонкретный Socket либо null/пропущен для общего кода расширения. Nullable с PHP 8.
РезультатЦелочисленный код; 0 — ошибки не было либо её явно очистили.
ГраницаЧтение не очищает код, успешный последующий вызов не следует считать автоматическим сбросом истории.
Временный отказ чтения
  1. Non-blocking receiver пока пуст
  2. read вернул false
  3. last_error(receiver) даёт would-block
  4. Повторное чтение кода сохраняет значение

Этот вариант. Коды ОС различаются численно, поэтому сравниваем с доступными константами. Последовательные вызовы last_error не очищают записанную ошибку. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Прочитать код временного отказа нужного endpointСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_set_nonblock($receiver);
    $data = @socket_read($receiver, 8);
    if ($data !== false) throw new RuntimeException('Ожидалось отсутствие данных');
    $code = socket_last_error($receiver);
    $pending = $code === SOCKET_EWOULDBLOCK || (defined('SOCKET_EAGAIN') && $code === constant('SOCKET_EAGAIN'));
    echo $pending ? "Данные пока не готовы\n" : "Иная ошибка\n";
    echo socket_last_error($receiver) === $code ? "Чтение кода его не очищает\n" : "Код изменился\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Данные пока не готовы
Чтение кода его не очищает

Этот вариант. Общий код нужен, например, после неудачного create, когда объекта ещё нет, и для select. Сохранённый код не является непрерывным монитором здоровья сети; прежде проверяйте результат конкретного вызова.

Сбросить и проверить общий код перед отдельным этапомСамостоятельный пример
<?php
declare(strict_types=1);

socket_clear_error();
echo socket_last_error() === 0 ? "Общей сохранённой ошибки нет\n" : "Есть прежняя ошибка\n";
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException(socket_strerror(socket_last_error()));
try { echo socket_last_error($socket) === 0 ? "У нового endpoint код 0\n" : "Есть ошибка\n"; }
finally { socket_close($socket); }
Ожидаемый результат
Общей сохранённой ошибки нет
У нового endpoint код 0

Связанные действия: socket_clear_error, socket_strerror, socket_select.

socket_clear_errorЯвно сбросить сохранённую ошибку после обработки, не изменяя само соединение.

PHP 4.2.0+ · sockets

socket_clear_error(?Socket $socket = null): void

Отличить отсутствие данных сейчас от потерянного ответа. После неблокирующего чтения без данных системный код объясняет would-block. last_error читает его, strerror даёт описание ОС, clear_error убирает сохранённый код, но ничего не чинит в соединении.

socket_clear_error($client)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Новая конструкция именно здесьКак она участвует в обмене
set_error_handler(function ...)PHP вызывает этот обработчик при предупреждении внутри ограниченного опыта. Возврат true помечает предупреждение обработанным; restore_error_handler в finally возвращает прежнее поведение. Это не callback чтения сокета.
Отличить отсутствие данных сейчас от потерянного ответаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
socket_set_nonblock($client);
set_error_handler(function (int $level): bool { return $level === E_WARNING; });
try { $empty = socket_read($client, 1); } finally { restore_error_handler(); }
$code = socket_last_error($client);
if ($empty !== false || $code === 0) { throw new RuntimeException('Ожидалось отсутствие данных сейчас'); }
$diagnostic = ['code'=>$code, 'message'=>socket_strerror($code)];
if (file_put_contents(__DIR__ . '/socket-error.json', json_encode($diagnostic, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)) === false) { throw new RuntimeException('Диагностика не записана'); }
socket_clear_error($client);
socket_set_block($client);
echo "Ответ ещё не поступил; канал остаётся открыт\n";
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Ответ ещё не поступил; канал остаётся открыт
Молоток — 8 шт.

Ожидаемое предупреждение перехвачено только вокруг одной демонстрационной операции. socket-error.json сохраняет фактические code/message вашей ОС; затем сервер получает запрос и отвечает нормально.

Ошибки и следующий шаг. В приложении классифицируйте EAGAIN/EWOULDBLOCK и другие ошибки по доступным платформенным константам. Не подавляйте все ошибки вокруг всего цикла. Старый код мог сохраниться после успешной операции, поэтому явная очистка полезна перед диагностикой.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Явно сбросить сохранённую ошибку после обработки, не изменяя само соединение.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketSocket для его кода; null/пропущен для общего кода.
Результатvoid; следующий last_error соответствующей области вернёт 0.
ГраницаНе заменяет исправление причины, повторное подключение или обработку partial data.
Очистка истории
  1. Read сообщил временный отказ
  2. Код прочитан
  3. clear_error(receiver)
  4. last_error(receiver) теперь 0

Этот вариант. Сначала причина прочитана и обработана, затем очищена. clear_error не добавляет данные в очередь и не ремонтирует соединение. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Очистить ошибку после её осмысленной обработкиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_set_nonblock($receiver);
    $data = @socket_read($receiver, 8);
    if ($data !== false) throw new RuntimeException('Ожидалось отсутствие данных');
    $code = socket_last_error($receiver);
    if ($code === 0) throw new RuntimeException('Нет кода отказа');
    socket_clear_error($receiver);
    echo socket_last_error($receiver) === 0 ? "История endpoint очищена\n" : "Код не сброшен\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
История endpoint очищена

Этот вариант. Без аргумента сбрасывается общий код расширения. С объектом сбрасывается его собственный код. Это два разных места хранения; выбор должен соответствовать операции, ошибку которой вы читаете. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отделить общий код от кода конкретного socketСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_set_nonblock($receiver);
    $data = @socket_read($receiver, 8);
    if ($data !== false) throw new RuntimeException('Ожидалось отсутствие данных');
    $specific = socket_last_error($receiver);
    socket_clear_error();
    echo socket_last_error() === 0 ? "Общий код сброшен\n" : "Не сброшен\n";
    echo socket_last_error($receiver) === $specific ? "Код endpoint сохранён\n" : "Изменён\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
Общий код сброшен
Код endpoint сохранён

Связанные действия: socket_last_error, socket_strerror.

socket_strerrorПреобразовать системный код ошибки в описание для диагностики.

PHP 4.1.0+ · sockets

socket_strerror(int $error_code): string

Отличить отсутствие данных сейчас от потерянного ответа. После неблокирующего чтения без данных системный код объясняет would-block. last_error читает его, strerror даёт описание ОС, clear_error убирает сохранённый код, но ничего не чинит в соединении.

socket_strerror($code)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Новая конструкция именно здесьКак она участвует в обмене
set_error_handler(function ...)PHP вызывает этот обработчик при предупреждении внутри ограниченного опыта. Возврат true помечает предупреждение обработанным; restore_error_handler в finally возвращает прежнее поведение. Это не callback чтения сокета.
Отличить отсутствие данных сейчас от потерянного ответаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
socket_set_nonblock($client);
set_error_handler(function (int $level): bool { return $level === E_WARNING; });
try { $empty = socket_read($client, 1); } finally { restore_error_handler(); }
$code = socket_last_error($client);
if ($empty !== false || $code === 0) { throw new RuntimeException('Ожидалось отсутствие данных сейчас'); }
$diagnostic = ['code'=>$code, 'message'=>socket_strerror($code)];
if (file_put_contents(__DIR__ . '/socket-error.json', json_encode($diagnostic, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)) === false) { throw new RuntimeException('Диагностика не записана'); }
socket_clear_error($client);
socket_set_block($client);
echo "Ответ ещё не поступил; канал остаётся открыт\n";
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Ответ ещё не поступил; канал остаётся открыт
Молоток — 8 шт.

Ожидаемое предупреждение перехвачено только вокруг одной демонстрационной операции. socket-error.json сохраняет фактические code/message вашей ОС; затем сервер получает запрос и отвечает нормально.

Ошибки и следующий шаг. В приложении классифицируйте EAGAIN/EWOULDBLOCK и другие ошибки по доступным платформенным константам. Не подавляйте все ошибки вокруг всего цикла. Старый код мог сохраниться после успешной операции, поэтому явная очистка полезна перед диагностикой.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Преобразовать системный код ошибки в описание для диагностики.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
error_codeКод от socket_last_error либо известная SOCKET_* константа, не HTTP-статус.
РезультатСтрока системного описания, зависящая от ОС/локали (LC_MESSAGES там, где применимо).
ГраницаСравнение/ветвление делают по коду; одинаковый текст между Windows и Unix не гарантирован.
Из кода в пояснение
  1. SOCKET_ECONNREFUSED задаёт вид отказа
  2. strerror запрашивает текст ОС
  3. Непустая строка пригодна для журнала
  4. Программа продолжает сравнивать числовой код

Этот вариант. Само описание зависит от ОС и локали, поэтому одинаковую английскую/русскую фразу всем платформам не обещаем. Чтобы увидеть её на своём компьютере, выведите $description. Соединение здесь не выполняется.

Получить понятное описание известного кодаСамостоятельный пример
<?php
declare(strict_types=1);

$description = socket_strerror(SOCKET_ECONNREFUSED);
echo is_string($description) && $description !== ''
    ? "Для отказа подключения получено описание ОС\n" : "Нет описания\n";
Ожидаемый результат
Для отказа подключения получено описание ОС

Этот вариант. Для решения программы используйте код и контекст операции, а не парсинг локализованной строки. В пользовательский ответ не обязательно выводить внутренние адреса/детали ОС; журнал и публичное сообщение имеют разные задачи. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Подготовить диагностическую запись после отказаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    socket_set_nonblock($receiver);
    $data = @socket_read($receiver, 8);
    if ($data !== false) throw new RuntimeException('Ожидалось отсутствие данных');
    $code = socket_last_error($receiver);
    $diagnostic = ['operation' => 'read-stock', 'code' => $code, 'message' => socket_strerror($code)];
    echo $diagnostic['operation'], "\n";
    echo $diagnostic['code'] !== 0 && $diagnostic['message'] !== '' ? "Код и описание сохранены\n" : "Диагностика пуста\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
read-stock
Код и описание сохранены

Связанные действия: socket_last_error, socket_clear_error.

socket_addrinfo_lookupПолучить варианты адреса для будущего bind/connect, ещё не открывая соединение.

PHP 7.2+ · sockets

socket_addrinfo_lookup(string $host, ?string $service = null, array $hints = []): array|false

Связать описание адреса с работающим каналом каталога. lookup создаёт кандидатов адреса для указанных семейства/типа. Числовой loopback и AI_NUMERICHOST не требуют внешнего DNS.

socket_addrinfo_lookup(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Связать описание адреса с работающим каналом каталогаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_protocol'=>SOL_TCP, 'ai_flags'=>AI_NUMERICHOST]);
if ($items === false || $items === []) { throw new RuntimeException('Нет описания адреса'); }
$details = socket_addrinfo_explain($items[0]);
if ($details['ai_addr']['sin_addr'] !== '127.0.0.1') { throw new RuntimeException('Не локальный адрес'); }
$listener = socket_addrinfo_bind($items[0]);
if ($listener === false || !socket_listen($listener, 2)) { throw new RuntimeException('Слушатель не создан'); }
socket_getsockname($listener, $host, $port);
$targets = socket_addrinfo_lookup($host, (string)$port, ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_flags'=>AI_NUMERICHOST]);
if ($targets === false || $targets === []) { throw new RuntimeException('Нет адреса назначения'); }
$client = socket_addrinfo_connect($targets[0]);
if ($client === false) { throw new RuntimeException('Клиент не создан'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сначала AddressInfo описывает 127.0.0.1:0. После bind ОС выбирает порт; getsockname получает реальный номер, новый lookup подготавливает цель клиента. Только после connect/accept передаются данные Молотка.

Ошибки и следующий шаг. Не сериализуйте Socket/AddressInfo как данные задания. Набор кандидатов может быть пуст/недоступен; имена с несколькими адресами требуют перебора кандидатов и согласованного срока. Для ручного nonblocking-connect используйте отдельные create/connect/select/SO_ERROR.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Получить варианты адреса для будущего bind/connect, ещё не открывая соединение.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
hostИмя узла или числовой адрес; для учебных примеров только 127.0.0.1.
serviceНомер порта строкой/имя службы/null.
hintsАссоциативный массив только четырёх перечисленных ключей.
РезультатМассив AddressInfo с PHP 8; раньше resources. false при неудаче resolver.
ОшибкиTypeError/ValueError при недопустимых ключах, типах, диапазонах hints; resolver false не обещает socket_last_error.
Пока только описание
  1. 127.0.0.1 + строка 8080
  2. hints ограничивают IPv4 TCP
  3. AddressInfo хранит результат
  4. explain показывает адрес
  5. connect ещё не вызывался

Этот вариант. Массив hints ограничивает поиск IPv4 и потоком. Числовые флаги запрещают разрешение имени узла/службы. Результат пока только описывает адрес; к порту 8080 никто не подключался.

Описать локальный IPv4 адрес без DNSСамостоятельный пример
<?php
declare(strict_types=1);

$items = socket_addrinfo_lookup('127.0.0.1', '8080', [
    'ai_family' => AF_INET, 'ai_socktype' => SOCK_STREAM,
    'ai_flags' => AI_NUMERICHOST | AI_NUMERICSERV,
]);
if ($items === false || $items === []) throw new RuntimeException('Нет адреса');
$view = socket_addrinfo_explain($items[0]);
echo $view['ai_addr']['sin_addr'], ':', $view['ai_addr']['sin_port'], "\n";
Ожидаемый результат
127.0.0.1:8080

Этот вариант. В PHP 8.5 неизвестный ключ hints — ValueError. Не передавайте сюда произвольный массив из запроса. Обычный неудачный поиск возвращает false и не обязан обновлять socket_last_error: старый код ошибки сокета не является диагнозом DNS.

Отличить опечатку настройки от отсутствующего адресаСамостоятельный пример
<?php
declare(strict_types=1);

try {
    socket_addrinfo_lookup('127.0.0.1', '80', ['family' => AF_INET]);
} catch (ValueError $error) {
    echo "Ключ называется ai_family\n";
}
Ожидаемый результат
Ключ называется ai_family
Ключ hintsСмысл
ai_familyAF_INET либо AF_INET6. Чтобы не ограничивать семейство, пропустите ключ; явное 0/AF_UNSPEC в PHP 8.5 не проходит проверку.
ai_socktypeSOCK_STREAM/SOCK_DGRAM и др.; 0 не ограничивает тип.
ai_protocolSOL_TCP/SOL_UDP и др.; 0 не ограничивает протокол.
ai_flagsБитовая комбинация AI_*: NUMERICHOST/NUMERICSERV, PASSIVE для bind, CANONNAME, ADDRCONFIG, V4MAPPED/ALL и платформенные IDN-флаги.

service — строка номера порта либо имя службы из базы ОС, например http. Это не путь URL. null оставляет службу неуказанной. У одного имени бывают несколько IPv4/IPv6 кандидатов: первый может оказаться недоступен, поэтому прикладной клиент пробует допустимые кандидаты по политике и общему сроку. AF_UNIX не принимается этим resolver API. hints не задаёт таймаут DNS или подключения; для защищённого клиента также нужна проверка адресов и повторных разрешений, иначе возникает SSRF.

Связанные действия: socket_addrinfo_explain, socket_addrinfo_bind, socket_addrinfo_connect.

socket_addrinfo_explainПоказать человеку адрес, семейство и протокол кандидата.

PHP 7.2+ · sockets

socket_addrinfo_explain(AddressInfo $address): array

Связать описание адреса с работающим каналом каталога. explain превращает непрозрачный AddressInfo в обычный массив ai_* для диагностики. Изменение массива не меняет сам AddressInfo.

socket_addrinfo_explain(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Связать описание адреса с работающим каналом каталогаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_protocol'=>SOL_TCP, 'ai_flags'=>AI_NUMERICHOST]);
if ($items === false || $items === []) { throw new RuntimeException('Нет описания адреса'); }
$details = socket_addrinfo_explain($items[0]);
if ($details['ai_addr']['sin_addr'] !== '127.0.0.1') { throw new RuntimeException('Не локальный адрес'); }
$listener = socket_addrinfo_bind($items[0]);
if ($listener === false || !socket_listen($listener, 2)) { throw new RuntimeException('Слушатель не создан'); }
socket_getsockname($listener, $host, $port);
$targets = socket_addrinfo_lookup($host, (string)$port, ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_flags'=>AI_NUMERICHOST]);
if ($targets === false || $targets === []) { throw new RuntimeException('Нет адреса назначения'); }
$client = socket_addrinfo_connect($targets[0]);
if ($client === false) { throw new RuntimeException('Клиент не создан'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сначала AddressInfo описывает 127.0.0.1:0. После bind ОС выбирает порт; getsockname получает реальный номер, новый lookup подготавливает цель клиента. Только после connect/accept передаются данные Молотка.

Ошибки и следующий шаг. Не сериализуйте Socket/AddressInfo как данные задания. Набор кандидатов может быть пуст/недоступен; имена с несколькими адресами требуют перебора кандидатов и согласованного срока. Для ручного nonblocking-connect используйте отдельные create/connect/select/SO_ERROR.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Показать человеку адрес, семейство и протокол кандидата.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
addressОдин элемент socket_addrinfo_lookup, не весь массив.
РезультатОбычный массив с ai_* и вложенным ai_addr.
Ошибка/версияНеверный класс — TypeError; объект AddressInfo с PHP 8, прежде resource.
Кандидат UDP
  1. lookup создаёт AddressInfo
  2. explain возвращает массив
  3. ai_family означает IPv4
  4. ai_socktype означает датаграммы
  5. sin_port равен 9000

Этот вариант. Ключи ai_* описывают договор адреса; sin_port уже целое число в привычном порядке байтов. Читать внутренние свойства AddressInfo не нужно.

Прочитать поля непрозрачного результатаСамостоятельный пример
<?php
declare(strict_types=1);

$items = socket_addrinfo_lookup('127.0.0.1', '9000', ['ai_family' => AF_INET, 'ai_socktype' => SOCK_DGRAM]);
if ($items === false || $items === []) throw new RuntimeException('Нет адреса');
$info = socket_addrinfo_explain($items[0]);
echo $info['ai_family'] === AF_INET ? "IPv4\n" : "Иное семейство\n";
echo $info['ai_socktype'] === SOCK_DGRAM ? "Датаграммы\n" : "Иной тип\n";
echo $info['ai_addr']['sin_port'], "\n";
Ожидаемый результат
IPv4
Датаграммы
9000

Этот вариант. Ветка IPv6 добавляет скобки в подпись адрес:порт. Это формат журнала, а не аргумент socket_connect: туда IPv6 передают без скобок.

Сделать безопасную подпись кандидата для журналаСамостоятельный пример
<?php
declare(strict_types=1);

function candidateLabel(AddressInfo $candidate): string {
    $data = socket_addrinfo_explain($candidate);
    if ($data['ai_family'] === AF_INET) {
        return $data['ai_addr']['sin_addr'] . ':' . $data['ai_addr']['sin_port'];
    }
    return '[' . $data['ai_addr']['sin6_addr'] . ']:' . $data['ai_addr']['sin6_port'];
}
$items = socket_addrinfo_lookup('127.0.0.1', '8081', ['ai_family' => AF_INET, 'ai_socktype' => SOCK_STREAM]);
if ($items === false || $items === []) throw new RuntimeException('Нет кандидатов');
echo candidateLabel($items[0]), "\n";
Ожидаемый результат
127.0.0.1:8081

Результат содержит ai_flags, ai_family, ai_socktype, ai_protocol, ai_addr и при наличии ai_canonname. В IPv4 ai_addr содержит sin_addr/sin_port, в IPv6 — sin6_addr/sin6_port. Не переносите сюда автоматически поля flowinfo/scope_id из структуры sendmsg: текущий explain их не выдаёт. Изменение возвращённого массива не меняет исходный AddressInfo; он остаётся непрозрачным описанием resolver.

Связанные действия: socket_addrinfo_lookup, AddressInfo.

socket_addrinfo_bindСоздать и привязать endpoint, используя уже найденное сочетание адреса, типа и протокола.

PHP 7.2+ · sockets

socket_addrinfo_bind(AddressInfo $address): Socket|false

Связать описание адреса с работающим каналом каталога. bind создаёт и привязывает Socket по одному готовому AddressInfo; listen всё равно выполняется отдельно.

socket_addrinfo_bind(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Связать описание адреса с работающим каналом каталогаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_protocol'=>SOL_TCP, 'ai_flags'=>AI_NUMERICHOST]);
if ($items === false || $items === []) { throw new RuntimeException('Нет описания адреса'); }
$details = socket_addrinfo_explain($items[0]);
if ($details['ai_addr']['sin_addr'] !== '127.0.0.1') { throw new RuntimeException('Не локальный адрес'); }
$listener = socket_addrinfo_bind($items[0]);
if ($listener === false || !socket_listen($listener, 2)) { throw new RuntimeException('Слушатель не создан'); }
socket_getsockname($listener, $host, $port);
$targets = socket_addrinfo_lookup($host, (string)$port, ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_flags'=>AI_NUMERICHOST]);
if ($targets === false || $targets === []) { throw new RuntimeException('Нет адреса назначения'); }
$client = socket_addrinfo_connect($targets[0]);
if ($client === false) { throw new RuntimeException('Клиент не создан'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сначала AddressInfo описывает 127.0.0.1:0. После bind ОС выбирает порт; getsockname получает реальный номер, новый lookup подготавливает цель клиента. Только после connect/accept передаются данные Молотка.

Ошибки и следующий шаг. Не сериализуйте Socket/AddressInfo как данные задания. Набор кандидатов может быть пуст/недоступен; имена с несколькими адресами требуют перебора кандидатов и согласованного срока. Для ручного nonblocking-connect используйте отдельные create/connect/select/SO_ERROR.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Создать и привязать endpoint, используя уже найденное сочетание адреса, типа и протокола.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
addressКандидат собственного адреса из lookup.
РезультатНовый Socket с привязанным адресом либо false. Listen не выполняется.
ОшибкиСистемный отказ создания/bind: warning и false; неверный класс: TypeError.
ВерсииPHP 7.2; вход/выход объекты с PHP 8.
UDP bind из описания
  1. lookup: 127.0.0.1:0 + DGRAM
  2. addrinfo_bind создаёт Socket
  3. ОС выбирает свободный порт
  4. getsockname читает фактический порт

Этот вариант. bind создаёт Socket и привязывает его одним вызовом. Исходный AddressInfo с портом 0 остаётся описанием запроса; фактический порт надо читать у Socket.

Привязать UDP endpoint по подготовленному описаниюСамостоятельный пример
<?php
declare(strict_types=1);

$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family' => AF_INET, 'ai_socktype' => SOCK_DGRAM]);
if ($items === false || $items === []) throw new RuntimeException('Нет адреса');
$socket = socket_addrinfo_bind($items[0]);
if ($socket === false) throw new RuntimeException('Не удалось привязать');
try {
    socket_getsockname($socket, $address, $port);
    echo $address, "\n", $port > 0 ? "Порт выбран ОС\n" : "Ошибка\n";
} finally { socket_close($socket); }
Ожидаемый результат
127.0.0.1
Порт выбран ОС

Этот вариант. Функция не вызывает listen. Отдельный шаг делает слушающий сервер из привязанного TCP endpoint. Здесь ни один публичный интерфейс не открывается.

После привязки отдельно включить очередь TCPСамостоятельный пример
<?php
declare(strict_types=1);

$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family' => AF_INET, 'ai_socktype' => SOCK_STREAM]);
if ($items === false || $items === []) throw new RuntimeException('Нет кандидата');
$listener = socket_addrinfo_bind($items[0]);
if ($listener === false) throw new RuntimeException('Нет endpoint');
try {
    if (!socket_listen($listener, 2)) throw new RuntimeException('Нет очереди');
    echo "TCP listener создан из AddressInfo\n";
} finally { socket_close($listener); }
Ожидаемый результат
TCP listener создан из AddressInfo

Связанные действия: socket_addrinfo_lookup, socket_listen, socket_getsockname.

socket_addrinfo_connectСоздать подключённый endpoint по одному кандидату адреса.

PHP 7.2+ · sockets

socket_addrinfo_connect(AddressInfo $address): Socket|false

Связать описание адреса с работающим каналом каталога. connect создаёт новый Socket и подключает его по одному кандидату. Это удобство, но не управляемый таймаут подключения.

socket_addrinfo_connect(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Связать описание адреса с работающим каналом каталогаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_protocol'=>SOL_TCP, 'ai_flags'=>AI_NUMERICHOST]);
if ($items === false || $items === []) { throw new RuntimeException('Нет описания адреса'); }
$details = socket_addrinfo_explain($items[0]);
if ($details['ai_addr']['sin_addr'] !== '127.0.0.1') { throw new RuntimeException('Не локальный адрес'); }
$listener = socket_addrinfo_bind($items[0]);
if ($listener === false || !socket_listen($listener, 2)) { throw new RuntimeException('Слушатель не создан'); }
socket_getsockname($listener, $host, $port);
$targets = socket_addrinfo_lookup($host, (string)$port, ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_flags'=>AI_NUMERICHOST]);
if ($targets === false || $targets === []) { throw new RuntimeException('Нет адреса назначения'); }
$client = socket_addrinfo_connect($targets[0]);
if ($client === false) { throw new RuntimeException('Клиент не создан'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сначала AddressInfo описывает 127.0.0.1:0. После bind ОС выбирает порт; getsockname получает реальный номер, новый lookup подготавливает цель клиента. Только после connect/accept передаются данные Молотка.

Ошибки и следующий шаг. Не сериализуйте Socket/AddressInfo как данные задания. Набор кандидатов может быть пуст/недоступен; имена с несколькими адресами требуют перебора кандидатов и согласованного срока. Для ручного nonblocking-connect используйте отдельные create/connect/select/SO_ERROR.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Создать подключённый endpoint по одному кандидату адреса.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
addressОдин кандидат назначения lookup.
РезультатНовый Socket либо false; для TCP успешный connect, для UDP установлен peer.
ОшибкиWarning/false при отказе создания или подключения; TypeError для неверного входа.
ОграничениеВызов может блокироваться по сетевой политике ОС; собственной настройки срока здесь нет.
Три байта локальному серверу
  1. Создан собственный listener
  2. lookup описывает его порт
  3. addrinfo_connect создаёт клиента
  4. accept получает серверную сторону
  5. A42 доходит до peer

Этот вариант. Весь обмен остаётся в одном процессе и на loopback. Приведение порта к string нужно для service; connect создаёт новый объект, которому затем отправляют три байта. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Подключиться к собственному TCP listener через AddressInfoСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
$listener = labListener(); socket_getsockname($listener, $address, $port);
$items = socket_addrinfo_lookup($address, (string)$port, ['ai_family' => AF_INET, 'ai_socktype' => SOCK_STREAM]);
if ($items === false || $items === []) throw new RuntimeException('Нет адреса');
$client = socket_addrinfo_connect($items[0]);
if ($client === false) throw new RuntimeException('Нет подключения');
$peer = socket_accept($listener);
if ($peer === false) throw new RuntimeException('Нет клиента');
try { labWriteAll($client, 'A42'); echo labReadExact($peer, 3), "\n"; }
finally { labClose($peer, $client, $listener); }
Ожидаемый результат
A42

Этот вариант. Для SOCK_DGRAM connect выбирает адрес по умолчанию и фильтрует peer, но не подтверждает наличие/доступность сервиса. Приём локального пакета доказывает лишь этот учебный обмен. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Установить адрес назначения для UDP без TCP handshakeСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $unused, $port] = labUdp(); socket_close($unused);
$items = socket_addrinfo_lookup('127.0.0.1', (string)$port, ['ai_family' => AF_INET, 'ai_socktype' => SOCK_DGRAM]);
if ($items === false || $items === []) throw new RuntimeException('Нет адреса');
$sender = socket_addrinfo_connect($items[0]);
if ($sender === false) throw new RuntimeException('Нет endpoint');
try {
    if (socket_send($sender, 'T=21', 4, 0) !== 4) throw new RuntimeException('Нет отправки');
    if (socket_recvfrom($receiver, $data, 32, 0, $from, $fromPort) === false) throw new RuntimeException('Нет данных');
    echo $data, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
T=21

Это удобное сочетание create+connect, но до соединения у вас ещё нет Socket для задания non-blocking режима. Для управляемого срока подключения используйте отдельные create, set_nonblock, connect, select и проверку SO_ERROR. Не обещайте таймаут через последующий SO_RCVTIMEO: это другая стадия.

Связанные действия: socket_addrinfo_lookup, socket_connect, socket_set_nonblock.

SocketРазличать конечная точка и обычный пользовательский объект с открытыми свойствами.

PHP 8.0+ · sockets

final class Socket { /* opaque */ }

Связать описание адреса с работающим каналом каталога. Socket — живая конечная точка. Её получают функциями расширения, передают в I/O и закрывают; обычный new Socket недоступен.

Socket(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Связать описание адреса с работающим каналом каталогаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_protocol'=>SOL_TCP, 'ai_flags'=>AI_NUMERICHOST]);
if ($items === false || $items === []) { throw new RuntimeException('Нет описания адреса'); }
$details = socket_addrinfo_explain($items[0]);
if ($details['ai_addr']['sin_addr'] !== '127.0.0.1') { throw new RuntimeException('Не локальный адрес'); }
$listener = socket_addrinfo_bind($items[0]);
if ($listener === false || !socket_listen($listener, 2)) { throw new RuntimeException('Слушатель не создан'); }
socket_getsockname($listener, $host, $port);
$targets = socket_addrinfo_lookup($host, (string)$port, ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_flags'=>AI_NUMERICHOST]);
if ($targets === false || $targets === []) { throw new RuntimeException('Нет адреса назначения'); }
$client = socket_addrinfo_connect($targets[0]);
if ($client === false) { throw new RuntimeException('Клиент не создан'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сначала AddressInfo описывает 127.0.0.1:0. После bind ОС выбирает порт; getsockname получает реальный номер, новый lookup подготавливает цель клиента. Только после connect/accept передаются данные Молотка.

Ошибки и следующий шаг. Не сериализуйте Socket/AddressInfo как данные задания. Набор кандидатов может быть пуст/недоступен; имена с несколькими адресами требуют перебора кандидатов и согласованного срока. Для ручного nonblocking-connect используйте отдельные create/connect/select/SO_ERROR.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Различать конечная точка и обычный пользовательский объект с открытыми свойствами.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
Созданиеsocket_create/accept и другие фабрики Socket; socket_addrinfo_lookup для AddressInfo.
Публичный интерфейсНи пользовательского конструктора, ни полей/методов: используйте функции расширения.
ВерсияPHP 8: замена соответствующего resource; is_resource больше не является правильной проверкой.
Фабрика вместо new
  1. socket_create создаёт результат
  2. Socket скрывает внутреннюю структуру
  3. instanceof подтверждает тип
  4. Дальше вызываются функции sockets

Этот вариант. instanceof проверяет объект специального класса. Его внутренний системный ресурс скрыт: взаимодействовать с ним нужно функциями sockets.

Получить объект через функцию расширенияСамостоятельный пример
<?php
declare(strict_types=1);

$item = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($item === false) throw new RuntimeException('Нет сокета');
echo $item instanceof Socket ? "Socket: конечная точка\n" : "Ошибка\n";
socket_close($item);
Ожидаемый результат
Socket: конечная точка

Этот вариант. Класс final и непрозрачный: у него нет пользовательского конструктора, полей настройки или метода connect. Error здесь пойман ради демонстрации неправильного способа создания.

Понять, почему обычный new здесь не подходитСамостоятельный пример
<?php
declare(strict_types=1);

try { $item = new Socket(); }
catch (Error $error) { echo "Используйте фабрику расширения\n"; }
Ожидаемый результат
Используйте фабрику расширения

Socket хранит живой endpoint; AddressInfo хранит результат поиска адреса. Присваивание объекта другой переменной не копирует системный endpoint. Для Socket нужен явный close в finally, когда обмен завершён; AddressInfo не имеет функции close и освобождается обычным временем жизни объекта. Эти внутренние объекты нельзя использовать как сериализованный транспорт между процессами.

Связанные действия: socket_create, socket_close.

AddressInfoРазличать описание адреса и обычный пользовательский объект с открытыми свойствами.

PHP 8.0+ · sockets

final class AddressInfo { /* opaque */ }

Связать описание адреса с работающим каналом каталога. AddressInfo — описание адреса, полученное фабрикой lookup. Оно ещё не является открытым каналом и не имеет пользовательского конструктора.

AddressInfo(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Связать описание адреса с работающим каналом каталогаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$items = socket_addrinfo_lookup('127.0.0.1', '0', ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_protocol'=>SOL_TCP, 'ai_flags'=>AI_NUMERICHOST]);
if ($items === false || $items === []) { throw new RuntimeException('Нет описания адреса'); }
$details = socket_addrinfo_explain($items[0]);
if ($details['ai_addr']['sin_addr'] !== '127.0.0.1') { throw new RuntimeException('Не локальный адрес'); }
$listener = socket_addrinfo_bind($items[0]);
if ($listener === false || !socket_listen($listener, 2)) { throw new RuntimeException('Слушатель не создан'); }
socket_getsockname($listener, $host, $port);
$targets = socket_addrinfo_lookup($host, (string)$port, ['ai_family'=>AF_INET, 'ai_socktype'=>SOCK_STREAM, 'ai_flags'=>AI_NUMERICHOST]);
if ($targets === false || $targets === []) { throw new RuntimeException('Нет адреса назначения'); }
$client = socket_addrinfo_connect($targets[0]);
if ($client === false) { throw new RuntimeException('Клиент не создан'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Клиент не принят'); }
localTimeout($client);
localTimeout($peer);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Сначала AddressInfo описывает 127.0.0.1:0. После bind ОС выбирает порт; getsockname получает реальный номер, новый lookup подготавливает цель клиента. Только после connect/accept передаются данные Молотка.

Ошибки и следующий шаг. Не сериализуйте Socket/AddressInfo как данные задания. Набор кандидатов может быть пуст/недоступен; имена с несколькими адресами требуют перебора кандидатов и согласованного срока. Для ручного nonblocking-connect используйте отдельные create/connect/select/SO_ERROR.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Различать описание адреса и обычный пользовательский объект с открытыми свойствами.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
Созданиеsocket_create/accept и другие фабрики Socket; socket_addrinfo_lookup для AddressInfo.
Публичный интерфейсНи пользовательского конструктора, ни полей/методов: используйте функции расширения.
ВерсияPHP 8: замена соответствующего resource; is_resource больше не является правильной проверкой.
Фабрика вместо new
  1. socket_addrinfo_lookup создаёт результат
  2. AddressInfo скрывает внутреннюю структуру
  3. instanceof подтверждает тип
  4. Дальше вызываются функции sockets

Этот вариант. instanceof проверяет объект специального класса. Его внутренний системный ресурс скрыт: взаимодействовать с ним нужно функциями sockets.

Получить объект через функцию расширенияСамостоятельный пример
<?php
declare(strict_types=1);

$items = socket_addrinfo_lookup('127.0.0.1', '80', ['ai_family' => AF_INET, 'ai_socktype' => SOCK_STREAM]);
if ($items === false || $items === []) throw new RuntimeException('Нет адреса');
$item = $items[0];
echo $item instanceof AddressInfo ? "AddressInfo: описание адреса\n" : "Ошибка\n";
Ожидаемый результат
AddressInfo: описание адреса

Этот вариант. Класс final и непрозрачный: у него нет пользовательского конструктора, полей настройки или метода connect. Error здесь пойман ради демонстрации неправильного способа создания.

Понять, почему обычный new здесь не подходитСамостоятельный пример
<?php
declare(strict_types=1);

try { $item = new AddressInfo(); }
catch (Error $error) { echo "Используйте фабрику расширения\n"; }
Ожидаемый результат
Используйте фабрику расширения

Socket хранит живой endpoint; AddressInfo хранит результат поиска адреса. Присваивание объекта другой переменной не копирует системный endpoint. Для Socket нужен явный close в finally, когда обмен завершён; AddressInfo не имеет функции close и освобождается обычным временем жизни объекта. Эти внутренние объекты нельзя использовать как сериализованный транспорт между процессами.

Связанные действия: socket_addrinfo_lookup, socket_addrinfo_explain.

socket_get_optionПроверить фактическое свойство endpoint вместо предположения, что ОС приняла ровно запрошенное значение.

PHP 4.1.0+ · sockets

socket_get_option(Socket $socket, int $level, int $option): array|int|false

Прочитать действующую сетевую настройку перед ответом. Возвращаемая структура зависит от option. У SO_RCVTIMEO это sec/usec; get_option не устанавливает значение и не проверяет всю сеть.

socket_get_option($client, SOL_SOCKET, SO_RCVTIMEO)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Прочитать действующую сетевую настройку перед ответомСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
$timeout = socket_get_option($client, SOL_SOCKET, SO_RCVTIMEO);
if ($timeout === false || $timeout['sec'] !== 2) { throw new RuntimeException('Неожиданный срок чтения'); }
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

local-net.php настроил 2 секунды. Перед чтением проверяем этот конкретный договор, затем получаем реальный товар.

Ошибки и следующий шаг. getopt — точный псевдоним. Другие опции возвращают int,массив или иную описанную форму; не предполагайте одинаковые units и поля. Настройка I/O не ограничивает время connect.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Проверить фактическое свойство endpoint вместо предположения, что ОС приняла ровно запрошенное значение.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый Socket.
levelSOL_SOCKET для общих параметров; SOL_TCP, IPPROTO_IP, IPPROTO_IPV6 и другие уровни.
optionКонстанта параметра данного уровня; часть настроек поддерживает только чтение либо только запись.
РезультатЦелое значение, структурированный массив или false. SO_LINGER → l_onoff/l_linger; таймауты → sec/usec; PHP 8.5 FreeBSD TCP_FUNCTION_* → function_set_name/pcbcnt.
ОшибкаWarning/false при отказе ОС, Error для закрытого Socket; константа чужой платформы может отсутствовать.
Прочитать тип
  1. UDP endpoint уже создан
  2. level = SOL_SOCKET
  3. option = SO_TYPE
  4. Результат равен SOCK_DGRAM

Этот вариант. SOL_SOCKET выбирает общий уровень; SO_TYPE — его конкретное свойство. Это свойство только для чтения; записать новый тип вместо создания другого сокета нельзя.

Узнать тип уже созданного endpointСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет сокета');
try {
    $type = socket_get_option($socket, SOL_SOCKET, SO_TYPE);
    if ($type === false) throw new RuntimeException('Не прочитали тип');
    echo $type === SOCK_DGRAM ? "Endpoint передаёт датаграммы\n" : "Иной тип\n";
} finally { socket_close($socket); }
Ожидаемый результат
Endpoint передаёт датаграммы

Этот вариант. SO_LINGER возвращает массив, а не bool. Выключенная настройка не означает, что TCP гарантировал доставку последнего сообщения: прикладной ACK решает другую задачу.

Прочитать структурированную настройку закрытияСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) throw new RuntimeException('Нет сокета');
try {
    if (!socket_set_option($socket, SOL_SOCKET, SO_LINGER, ['l_onoff' => 0, 'l_linger' => 0])) throw new RuntimeException('Нет настройки');
    $linger = socket_get_option($socket, SOL_SOCKET, SO_LINGER);
    if ($linger === false) throw new RuntimeException('Нет результата');
    echo $linger['l_onoff'] === 0 ? "Дополнительное ожидание close выключено\n" : "Включено\n";
    echo 'Поле срока: ', $linger['l_linger'], "\n";
} finally { socket_close($socket); }
Ожидаемый результат
Дополнительное ожидание close выключено
Поле срока: 0

SO_ERROR читает и очищает отложенную ошибку ядра: после готовности неблокирующего connect это решающий тест, потому что готовность к записи сама по себе не доказывает успех. socket_last_error хранит отдельную ошибку PHP и не эквивалентен SO_ERROR. Размеры SO_SNDBUF/SO_RCVBUF ограничивает и иногда удваивает ОС; не проверяйте их на точное равенство запросу. Таймауты возвращаются массивом sec/usec и могут округляться.

Связанные действия: socket_set_option, socket_last_error, socket_getopt.

socket_set_optionИзменить конкретное свойство сокета: таймаут, буфер, TCP поведение или разрешённую multicast-настройку.

PHP 4.1.0+ · sockets

socket_set_option(Socket $socket, int $level, int $option, array|string|int $value): bool

Настроить TCP-канал перед маленьким запросом. Опции принадлежат уровню SOL_TCP/SOL_SOCKET и конкретному сокету. TCP_NODELAY отключает алгоритм Нейгла для малых записей; это настройка, не постоянное правило об ускорении.

socket_set_option($client, SOL_TCP, TCP_NODELAY, 1)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Настроить TCP-канал перед маленьким запросомСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
if (!socket_set_option($client, SOL_TCP, TCP_NODELAY, 1)) { throw new RuntimeException('TCP_NODELAY не применён'); }
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

После успешной настройки исполняется обычный запрос каталога. Корректность ответа не зависит от обещания микросекундного ускорения.

Ошибки и следующий шаг. setopt — точный псевдоним. Поддержка опций зависит от ОС; берите свои константы, не чужие числовые коды. Измеряйте работу протокола, прежде чем менять параметры.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Изменить конкретное свойство сокета: таймаут, буфер, TCP поведение или разрешённую multicast-настройку.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый endpoint; некоторые опции надо задавать до bind/connect.
level / optionУровень протокола + имя настройки; эти два целых значения нельзя переставлять.
valueЗависит от option: флаг 0/1, число, строка либо описанный массив. Произвольный JSON объект не является универсальным значением.
Результатtrue означает принятие настройки ОС; false + warning — отказ.
Ошибка/версияTypeError/ValueError для неверного договора; объект Socket с PHP 8; ряд новых параметров зависит от PHP 8.1–8.5 и ОС.
Две секунды для чтения
  1. Socket существует
  2. SO_RCVTIMEO выбирает чтение
  3. sec = 2, usec = 0
  4. set_option возвращает true
  5. get_option показывает принятую настройку

Этот вариант. Массив означает две секунды и ноль микросекунд. В примере никто не ждёт таймаут: мы конфигурируем endpoint и читаем его фактическую настройку. Это не общий срок всей операции и не срок DNS.

Задать предел одного блокирующего чтенияСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет сокета');
try {
    $ok = socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);
    if (!$ok) throw new RuntimeException('Таймаут не установлен');
    $timeout = socket_get_option($socket, SOL_SOCKET, SO_RCVTIMEO);
    echo $timeout['sec'] >= 1 ? "Предел ожидания чтения задан\n" : "Ошибка\n";
} finally { socket_close($socket); }
Ожидаемый результат
Предел ожидания чтения задан

Этот вариант. NODELAY влияет на TCP пакетизацию, но не создаёт границы сообщений и не обещает немедленную доставку. Разделитель строки остаётся частью нашего протокола. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отключить накопление мелких TCP записей для локального протоколаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$listener, $client, $peer] = labTcp();
try {
    if (!socket_set_option($client, SOL_TCP, TCP_NODELAY, 1)) throw new RuntimeException('Не удалось настроить TCP');
    echo socket_get_option($client, SOL_TCP, TCP_NODELAY) === 1 ? "Nagle выключен для клиента\n" : "Ошибка\n";
    labWriteAll($client, "A42\n"); echo labReadExact($peer, 4);
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
Nagle выключен для клиента
A42
Структурированное значениеКак прочитать
SO_LINGERМассив l_onoff (0/1), l_linger (срок ОС). Включённый linger с нулём может сделать закрытие abortive и потерять данные; не применять для «ускорения» вслепую.
SO_RCVTIMEO / SO_SNDTIMEOМассив sec/usec. В Windows значение переводится в миллисекунды, микросекундная точность не сохраняется. Нули обычно означают отсутствие такого таймаута.
MCAST_*group — адрес multicast, interface — индекс/имя интерфейса (0 — маршрут по умолчанию); source нужен у операций с источником. Семейство group должно соответствовать уровню IPPROTO_IP/IPPROTO_IPV6.
IP_MULTICAST_IF / IPV6_MULTICAST_IFPHP принимает индекс интерфейса либо имя; не копируйте структуру in_addr из C-примера.
SO_BINDTODEVICE / SO_ACCEPTFILTERСтрока устройства Linux / фильтра BSD; нужны поддержка ОС, права и допустимое имя.
TCP_CONGESTION / TCP_FUNCTION_BLKСтрока имени алгоритма Linux / блока TCP FreeBSD; последнее доступно с PHP 8.5. Неизвестное имя отвергает ОС.

Опции не имеют единого универсального default: он зависит от ОС, типа, протокола и системных настроек. Для используемых значений читайте get_option и проверяйте результат set_option. Наличие константы не гарантирует, что PHP преобразует любую сложную C-структуру соответствующего setsockopt. Полный каталог констант отделяет идентификаторы от форм поддерживаемых значений.

PHP 8.5 строже проверяет некорректные multicast-структуры и неподдерживаемые семейства; ошибки формы/диапазона могут быть TypeError/ValueError, а отказ ядра — warning/false. Не подавляйте предупреждение и затем продолжайте как будто настройка применена. REUSEADDR/REUSEPORT имеют разные правила ОС и могут менять изоляцию нескольких процессов; слушатель нашего примера обходится без них.

Связанные действия: socket_get_option, socket_select, socket_setopt.

socket_getoptПонимать краткое имя socket_get_option при чтении существующей программы.

PHP 4.1.0+ · sockets

socket_getopt(Socket $socket, int $level, int $option): array|int|false

Прочитать действующую сетевую настройку перед ответом. Возвращаемая структура зависит от option. У SO_RCVTIMEO это sec/usec; get_option не устанавливает значение и не проверяет всю сеть.

socket_get_option($client, SOL_SOCKET, SO_RCVTIMEO)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Прочитать действующую сетевую настройку перед ответомСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
$timeout = socket_get_option($client, SOL_SOCKET, SO_RCVTIMEO);
if ($timeout === false || $timeout['sec'] !== 2) { throw new RuntimeException('Неожиданный срок чтения'); }
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

local-net.php настроил 2 секунды. Перед чтением проверяем этот конкретный договор, затем получаем реальный товар.

Ошибки и следующий шаг. getopt — точный псевдоним. Другие опции возвращают int,массив или иную описанную форму; не предполагайте одинаковые units и поля. Настройка I/O не ограничивает время connect.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Понимать краткое имя socket_get_option при чтении существующей программы.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый Socket.
level / optionУровень протокола и его параметр.
РезультатТот же договор socket_get_option; int/array либо строго false.
Ошибки/версииТочно как у полной функции; короткое имя не отменяет проверки ОС и PHP.
Краткое имя, тот же endpoint
  1. Создан собственный Socket
  2. socket_getopt вызывается с SOL_SOCKET
  3. Операция использует тот же договор
  4. Поток байтов

Этот вариант. Это alias: та же реализация, а не отдельный режим с иными единицами измерения. В новом коде полное имя яснее.

Прочитать старое краткое имя в существующем кодеСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) throw new RuntimeException('Нет endpoint');
try { echo socket_getopt($socket, SOL_SOCKET, SO_TYPE) === SOCK_STREAM ? "Поток байтов\n" : "Ошибка\n"; }
finally { socket_close($socket); }
Ожидаемый результат
Поток байтов

Этот вариант. Строгое сравнение отличает значение 0 от false. Собственная функция задаёт политику приложения, а alias сохраняет договор полной функции.

Применить тот же договор в прикладной проверкеСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет endpoint');
try {
    if (!socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 0)) throw new RuntimeException('Не установлено');
    $value = socket_getopt($socket, SOL_SOCKET, SO_BROADCAST);
    if ($value === false) throw new RuntimeException('Ошибка чтения');
    echo $value === 0 ? "Ноль — корректное значение опции\n" : "Иное значение\n";
} finally { socket_close($socket); }
Ожидаемый результат
Ноль — корректное значение опции

Все уровни, значения, системные ограничения, возвраты и ошибки совпадают с socket_get_option. Название не помечено PHP.net устаревшим; его можно встретить в старых библиотеках. Не путайте sockets aliases с socket_set_timeout/socket_set_blocking: последние относятся к streams.

Связанные действия: socket_get_option, socket_close.

socket_setoptПонимать краткое имя socket_set_option при чтении существующей программы.

PHP 4.1.0+ · sockets

socket_setopt(Socket $socket, int $level, int $option, array|string|int $value): bool

Настроить TCP-канал перед маленьким запросом. Опции принадлежат уровню SOL_TCP/SOL_SOCKET и конкретному сокету. TCP_NODELAY отключает алгоритм Нейгла для малых записей; это настройка, не постоянное правило об ускорении.

socket_set_option($client, SOL_TCP, TCP_NODELAY, 1)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Настроить TCP-канал перед маленьким запросомСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
if (!socket_set_option($client, SOL_TCP, TCP_NODELAY, 1)) { throw new RuntimeException('TCP_NODELAY не применён'); }
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

После успешной настройки исполняется обычный запрос каталога. Корректность ответа не зависит от обещания микросекундного ускорения.

Ошибки и следующий шаг. setopt — точный псевдоним. Поддержка опций зависит от ОС; берите свои константы, не чужие числовые коды. Измеряйте работу протокола, прежде чем менять параметры.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Понимать краткое имя socket_set_option при чтении существующей программы.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый Socket.
level / optionУровень протокола и его параметр.
valueТот же договор socket_set_option; зависит от option, результат bool.
Ошибки/версииТочно как у полной функции; короткое имя не отменяет проверки ОС и PHP.
Краткое имя, тот же endpoint
  1. Создан собственный Socket
  2. socket_setopt вызывается с SOL_SOCKET
  3. Операция использует тот же договор
  4. Broadcast не разрешён

Этот вариант. Это alias: та же реализация, а не отдельный режим с иными единицами измерения. В новом коде полное имя яснее.

Прочитать старое краткое имя в существующем кодеСамостоятельный пример
<?php
declare(strict_types=1);

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет endpoint');
try {
    if (!socket_setopt($socket, SOL_SOCKET, SO_BROADCAST, 0)) throw new RuntimeException('Не удалось настроить');
    echo socket_get_option($socket, SOL_SOCKET, SO_BROADCAST) === 0 ? "Broadcast не разрешён\n" : "Ошибка\n";
} finally { socket_close($socket); }
Ожидаемый результат
Broadcast не разрешён

Этот вариант. Строгое сравнение отличает значение 0 от false. Собственная функция задаёт политику приложения, а alias сохраняет договор полной функции.

Применить тот же договор в прикладной проверкеСамостоятельный пример
<?php
declare(strict_types=1);

function boundRead(Socket $socket, int $seconds): void {
    if ($seconds < 1 || $seconds > 5) throw new InvalidArgumentException('Допустимо 1…5 секунд');
    if (!socket_setopt($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => $seconds, 'usec' => 0])) throw new RuntimeException('Не установлено');
}
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) throw new RuntimeException('Нет endpoint');
try { boundRead($socket, 1); echo "Политика локального чтения применена\n"; }
finally { socket_close($socket); }
Ожидаемый результат
Политика локального чтения применена

Все уровни, значения, системные ограничения, возвраты и ошибки совпадают с socket_set_option. Название не помечено PHP.net устаревшим; его можно встретить в старых библиотеках. Не путайте sockets aliases с socket_set_timeout/socket_set_blocking: последние относятся к streams.

Связанные действия: socket_set_option, socket_close.

socket_export_streamПодключить stream-функции к уже имеющемуся сокету, сохранив один endpoint.

PHP 7.0.7+ · sockets

socket_export_stream(Socket $socket): resource|false

Прочитать тот же сокет знакомыми stream-функциями. Экспорт даёт resource-представление той же конечной точки, а не второе соединение. Для чтения этого канала выбираем один API.

socket_export_stream($client)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Прочитать тот же сокет знакомыми stream-функциямиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
$stream = socket_export_stream($client);
if ($stream === false) { throw new RuntimeException('Не удалось получить stream'); }
stream_set_timeout($stream, 2);
if (fwrite($stream, "product:1\n") !== 10) { throw new RuntimeException('Запрос не записан'); }
serveProduct($peer);
$json = fgets($stream, 4097);
if ($json === false) { throw new RuntimeException('Нет ответа'); }
showProduct($json);
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

fwrite посылает запрос, fgets получает ответ с LF. Закрытие поручено Socket-владельцу; повторный fclose уже инвалидированного stream не нужен.

Ошибки и следующий шаг. Не чередуйте чтение через fread/socket_read без понимания буфера. Экспорт не добавляет TLS; ресурс идентифицирует сеть, но HTTP сам появится только при его кодировании.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Подключить stream-функции к уже имеющемуся сокету, сохранив один endpoint.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый Socket.
РезультатStream resource, связанный с тем же descriptor; при повторном вызове возвращается прежний stream. false при отказе.
ОшибкиError для закрытого объекта; системные/неподдержанные преобразования дают warning/false.
ВерсииPHP 7.0.7; Socket вместо resource с PHP 8.
Один endpoint, два представления
  1. Создана локальная пара
  2. export даёт stream для sender
  3. fwrite пишет A42
  4. receiver читает те же три байта
  5. Владелец закрывает endpoint один раз

Этот вариант. resource stream и объект Socket представляют один endpoint. Здесь закрытие поручено socket_close: повторный fclose того же ресурса не нужен. Для длинных записей нужен цикл обработки частичной записи. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Писать в Socket через знакомый stream APIСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$sender, $receiver] = labPair();
try {
    $stream = socket_export_stream($sender);
    if ($stream === false) throw new RuntimeException('Нет stream');
    if (fwrite($stream, 'A42') !== 3) throw new RuntimeException('Неполная запись');
    echo labReadExact($receiver, 3), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
A42

Этот вариант. Существующий stream возвращается повторно. Метаданные относятся к stream; его read timeout первоначально берётся из default_socket_timeout, поэтому настройки stream и sockets надо задавать явно в выбранном интерфейсе. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Убедиться, что повторный export не создаёт второе соединениеСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$a, $b] = labPair();
try {
    $first = socket_export_stream($a); $second = socket_export_stream($a);
    if ($first === false || $second === false) throw new RuntimeException('Нет stream');
    echo $first === $second ? "Это тот же stream\n" : "Иной ресурс\n";
    echo stream_get_meta_data($first)['blocked'] ? "Блокирующий режим\n" : "Неблокирующий режим\n";
} finally { labClose($a, $b); }
Ожидаемый результат
Это тот же stream
Блокирующий режим

Не чередуйте чтение через fread и socket_read без понимания буферизации: данные уже могли уйти в буфер одного слоя. Выберите один API для потока данных. socket_export_stream не добавляет TLS и не превращает байты в HTTP. Явное закрытие Socket инвалидирует связанный stream; обратное закрытие тоже делает дальнейший обмен через прежний endpoint недопустимым.

Связанные действия: socket_import_stream, socket_close.

socket_import_streamНастроить системный сокет, стоящий за существующим сетевым stream.

PHP 5.4+ · sockets

socket_import_stream(resource $stream): Socket|false

Настроить системный сокет существующего потока. Импорт даёт Socket для сетевой опции, которой может не быть в высокоуровневом интерфейсе. Он не копирует канал.

socket_import_stream($stream)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Настроить системный сокет существующего потокаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$listener = localListener();
socket_getsockname($listener, $host, $port);
$stream = stream_socket_client('tcp://'.$host.':'.$port, $number, $message, 1);
if ($stream === false) { throw new RuntimeException('Нет stream-клиента'); }
$client = socket_import_stream($stream);
if ($client === false) { throw new RuntimeException('Stream не является сетевым'); }
$peer = socket_accept($listener);
if ($peer === false) { throw new RuntimeException('Нет принятого клиента'); }
localTimeout($client);
localTimeout($peer);
socket_set_option($client, SOL_TCP, TCP_NODELAY, 1);
writeBytes($client, "product:1\n");
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Исходный TCP-stream связан с тем же Socket. Здесь канал нешифрован; для TLS надо оставить передачу байтов TLS-stream, а Socket использовать только для совместимых настроек.

Ошибки и следующий шаг. Обычный файловый resource не является сокетом. Владелец должен закрыть endpoint один раз; два представления не означают два независимых жизненных цикла.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Настроить системный сокет, стоящий за существующим сетевым stream.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
streamОткрытый stream с поддержкой преобразования в socket descriptor; файл/память не подходят.
РезультатSocket, связанный с тем же endpoint, либо false.
Ошибки/версияWarning/false для неподдержанного stream; TypeError для неверного типа. PHP 5.4; объект на выходе с PHP 8.
Настройка готового stream
  1. Локальный TCP stream уже открыт
  2. import даёт Socket того же соединения
  3. set_option задаёт TCP_NODELAY
  4. Проверяем фактическое значение

Этот вариант. Сначала создан собственный локальный сервер, поэтому внешний адрес не нужен. Импорт полезен для сетевой опции, которой нет в высокоуровневом API; чтение/запись приложения можно оставить stream-функциям. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Получить Socket для настройки существующего сетевого streamСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
$listener = labListener(); socket_getsockname($listener, $host, $port);
$stream = stream_socket_client("tcp://$host:$port", $number, $message, 2);
if ($stream === false) throw new RuntimeException('Нет stream-клиента');
$peer = socket_accept($listener);
if ($peer === false) throw new RuntimeException('Нет клиента');
$socket = socket_import_stream($stream);
if ($socket === false) throw new RuntimeException('Не сетевой stream');
try {
    if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) throw new RuntimeException('Нет настройки');
    echo socket_get_option($socket, SOL_TCP, TCP_NODELAY) === 1 ? "TCP stream настроен через Socket\n" : "Ошибка\n";
} finally { labClose($socket, $peer, $listener); }
Ожидаемый результат
TCP stream настроен через Socket

Этот вариант. @ здесь локально подавляет ожидаемое предупреждение демонстрации отказа; false всё равно проверяется. В приложении сохраните диагностику через свой обработчик. resource сам по себе ещё не означает сеть.

Не принимать обычный файловый stream за сокетСамостоятельный пример
<?php
declare(strict_types=1);

$file = fopen('php://temp', 'w+b');
if ($file === false) throw new RuntimeException('Нет временного stream');
try {
    $socket = @socket_import_stream($file);
    echo $socket === false ? "Этот stream не содержит сетевой descriptor\n" : "Неожиданный Socket\n";
} finally { fclose($file); }
Ожидаемый результат
Этот stream не содержит сетевой descriptor

Импорт не дублирует соединение и отключает read buffering на поддерживаемом stream. Если stream использует TLS, его шифрование реализовано слоем stream: низкоуровневое socket_read/socket_write не выполняет TLS-протокол. Для TLS оставляйте передачу данных stream API, а Socket используйте только для подходящих системных опций. Не сериализуйте Socket в очередь задач.

Связанные действия: socket_export_stream, socket_set_option.

socket_sendmsgПередать сообщение из нескольких буферов и, при необходимости, поддержанные ancillary-данные.

PHP 5.5+ · sockets

socket_sendmsg(Socket $socket, array $message, int $flags = 0): int|false

Получить одну строку инвентаризации отдельной UDP-датаграммой. UDP сохраняет границы датаграммы, но не гарантирует доставку/порядок. sendmsg собирает её из iov-буферов; recvmsg возвращает iov и метаданные массивом; sendto/recvfrom используют явный адрес.

socket_sendmsg(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Получить одну строку инвентаризации отдельной UDP-датаграммойСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$receiver = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
$sender = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
if (!socket_bind($receiver, '127.0.0.1', 0) || !socket_bind($sender, '127.0.0.1', 0)) { throw new RuntimeException('UDP не привязан'); }
localTimeout($receiver);
localTimeout($sender);
socket_getsockname($receiver, $host, $port);
$payload = "Молоток;8\n";
$sent = socket_sendmsg($sender, ['name'=>['addr'=>'127.0.0.1','port'=>$port], 'iov'=>['Молоток;', "8\n"]], 0);
if ($sent !== strlen($payload)) { throw new RuntimeException('Датаграмма не отправлена полностью'); }
$received = socket_recvfrom($receiver, $data, 1024, 0, $from, $fromPort);
if ($received === false || $data !== $payload) { throw new RuntimeException('Датаграмма не получена целиком'); }
echo "Получена строка инвентаризации:\n", $data;
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Получена строка инвентаризации:
Молоток;8

Полезный результат — реально полученные байты строки, не числовой успех отправки. Оба endpoint привязаны к loopback со свободными портами, предел приёма 1024 байта достаточен для известного сообщения.

Ошибки и следующий шаг. Слишком малый буфер может обрезать датаграмму; оставшуюся часть нельзя дочитать как TCP. Системные флаги усечения и доверие к адресу отправителя учитывают отдельно. Для изменения склада одного UDP без подтверждения/идемпотентности недостаточно.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Передать сообщение из нескольких буферов и, при необходимости, поддержанные ancillary-данные.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый подходящий endpoint.
messageНеобязательные name, iov, control; договор вложенных значений приведён ниже.
flags0 либо поддержанные MSG_* через |: OOB, EOR, DONTWAIT, DONTROUTE.
РезультатЧисло отправленных обычных байтов или false; TCP может принять только часть.
ОшибкиWarning/false при преобразовании/системном отказе; TypeError/ValueError для отдельных недопустимых значений.
Два буфера, одна датаграмма
  1. iov: A42= и 7
  2. name: собственный loopback port
  3. sendmsg отправляет 5 байтов
  4. recvfrom получает A42=7

Этот вариант. iov — список буферов; они идут по порядку в одном UDP сообщении. Индексы списка не отправляются. name задаёт получателя, flags передаётся отдельным третьим аргументом. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Собрать одну UDP датаграмму из двух строкСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    $count = socket_sendmsg($sender, ['name' => ['addr' => '127.0.0.1', 'port' => $port], 'iov' => ['A42=', '7']], 0);
    if ($count !== 5) throw new RuntimeException('Неполная отправка');
    if (socket_recvfrom($receiver, $data, 32, 0, $from, $fromPort) === false) throw new RuntimeException('Нет сообщения');
    echo $count, "\n", $data, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
5
A42=7

Этот вариант. У подключённого UDP endpoint уже есть адрес назначения. Это всё ещё UDP, а не подтверждение доставки в момент sendmsg. Датаграмму действительно получает только последующий recvfrom. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отправить сообщение выбранному UDP peer без поля nameСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    if (!socket_connect($sender, '127.0.0.1', $port)) throw new RuntimeException('Нет peer');
    if (socket_sendmsg($sender, ['iov' => ['T=', '21']], 0) !== 4) throw new RuntimeException('Не отправлено');
    if (socket_recvfrom($receiver, $data, 32, 0, $from, $fromPort) === false) throw new RuntimeException('Нет пакета');
    echo $data, "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
T=21
Поле messageТочный смысл
nameIPv4: addr/port; IPv6: addr/port/flowinfo/scope_id; Unix: path. Необязательный family выбирает семейство явно. Имя узла в addr запускает resolver; числовой loopback этого избегает.
iovСписок строковых буферов; ключи игнорируются, значения преобразуются в string. Без него/при пустом списке API не отправляет обычные данные и возвращает 0.
controlСписок массивов level/type/data. Поддерживаются только известные преобразователю PHP ancillary-структуры, не произвольный бинарный C struct.
Неизвестные ключиИгнорируются; поле flags внутри message не влияет на отправку. Флаги — третий аргумент.

Для SOL_SOCKET/SCM_RIGHTS data — непустой список Socket или stream; это передача открытых дескрипторов между доверенными Unix-процессами, не пересылка содержимого файла. Нужен хотя бы один обычный байт. SCM_CREDENTIALS (или системные SCM_CREDS/CREDS2) несёт pid/uid/gid. Для IPPROTO_IPV6: IPV6_PKTINFO принимает addr/ifindex, IPV6_HOPLIMIT и IPV6_TCLASS — int. Доступность определяется ОС. Получение показано рядом с recvmsg/cmsg_space.

Связанные действия: socket_recvmsg, socket_cmsg_space, socket_sendto.

socket_recvmsgПолучить байты, адрес и ancillary-данные одним системным чтением.

PHP 5.5+ · sockets

socket_recvmsg(Socket $socket, array &$message, int $flags = 0): int|false

Получить одну строку инвентаризации отдельной UDP-датаграммой. UDP сохраняет границы датаграммы, но не гарантирует доставку/порядок. sendmsg собирает её из iov-буферов; recvmsg возвращает iov и метаданные массивом; sendto/recvfrom используют явный адрес.

socket_recvmsg(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Получить одну строку инвентаризации отдельной UDP-датаграммойСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
$receiver = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
$sender = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
if (!socket_bind($receiver, '127.0.0.1', 0) || !socket_bind($sender, '127.0.0.1', 0)) { throw new RuntimeException('UDP не привязан'); }
localTimeout($receiver);
localTimeout($sender);
socket_getsockname($receiver, $host, $port);
$payload = "Молоток;8\n";
$sent = socket_sendto($sender, $payload, strlen($payload), 0, '127.0.0.1', $port);
if ($sent !== strlen($payload)) { throw new RuntimeException('Датаграмма не отправлена полностью'); }
$message = ['buffer_size'=>1024, 'controllen'=>64];
$received = socket_recvmsg($receiver, $message, 0);
$data = implode('', $message['iov'] ?? []);
if ($received === false || $data !== $payload) { throw new RuntimeException('Датаграмма не получена целиком'); }
echo "Получена строка инвентаризации:\n", $data;
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Получена строка инвентаризации:
Молоток;8

Полезный результат — реально полученные байты строки, не числовой успех отправки. Оба endpoint привязаны к loopback со свободными портами, предел приёма 1024 байта достаточен для известного сообщения.

Ошибки и следующий шаг. Слишком малый буфер может обрезать датаграмму; оставшуюся часть нельзя дочитать как TCP. Системные флаги усечения и доверие к адресу отправителя учитывают отдельно. Для изменения склада одного UDP без подтверждения/идемпотентности недостаточно.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Получить байты, адрес и ancillary-данные одним системным чтением.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketEndpoint с ожидаемым сообщением; blocking может ждать.
messageПередаваемая по ссылке переменная: сначала размеры/name, после вызова данные/control/flags.
flags0 либо поддержанные данным recvmsg флаги MSG_*, например PEEK/DONTWAIT.
РезультатКоличество обычных байтов или строго false; проверяйте также выходные flags.
ВерсииPHP 5.5; Socket вместо resource с PHP 8. Структура уточнена исходниками 8.5.11.
Массив меняет роль
  1. buffer_size=32 и controllen=64
  2. UDP A42=7 уже отправлена
  3. recvmsg заполняет iov
  4. Результат равен 5
  5. implode соединяет буферы для вывода

Этот вариант. До вызова массив задаёт размеры буферов; после вызова это уже полученное сообщение. controllen здесь небольшой резерв без запроса ancillary-опций; для конкретной структуры используйте cmsg_space. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Получить UDP сообщение через массив результатаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp();
try {
    if (socket_sendto($sender, 'A42=7', 5, 0, '127.0.0.1', $port) !== 5) throw new RuntimeException('Нет отправки');
    $message = ['buffer_size' => 32, 'controllen' => 64];
    $count = socket_recvmsg($receiver, $message, 0);
    if ($count === false) throw new RuntimeException('Нет сообщения');
    echo $count, "\n", implode('', $message['iov']), "\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
5
A42=7

Этот вариант. name резервирует получение адреса. flags — выходные признаки ядра; это не копия третьего аргумента. Полученный адрес идентифицирует endpoint, но не аутентифицирует человека или устройство. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Прочитать адрес отправителя и признаки обрезкиСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
[$receiver, $sender, $port] = labUdp(); socket_getsockname($sender, $sourceAddress, $sourcePort);
try {
    if (socket_sendto($sender, 'T=21', 4, 0, '127.0.0.1', $port) !== 4) throw new RuntimeException('Нет отправки');
    $message = ['name' => ['family' => AF_INET], 'buffer_size' => 64, 'controllen' => 64];
    if (socket_recvmsg($receiver, $message, 0) === false) throw new RuntimeException('Нет данных');
    echo $message['name']['addr'], "\n";
    echo $message['name']['port'] === $sourcePort ? "Порт отправителя совпал\n" : "Ошибка порта\n";
    echo ($message['flags'] & (MSG_TRUNC | MSG_CTRUNC)) === 0 ? "Данные и control не обрезаны\n" : "Обрезка\n";
} finally { labClose($sender, $receiver); }
Ожидаемый результат
127.0.0.1
Порт отправителя совпал
Данные и control не обрезаны

PHP.net оставляет эту страницу почти без описания; структура ниже сверена с tagged source PHP 8.5.11. Вход: buffer_size — ваш ограниченный размер обычных данных, controllen — обязательный положительный размер ancillary-буфера, name — необязательная структура семейства. Выход: name, control, iov и flags; входной массив заменяется. Нулевой результат означает EOF на TCP или допустимую пустую UDP датаграмму. MSG_TRUNC/MSG_CTRUNC требуют отдельной обработки: усечённую команду нельзя исполнять как полную.

control содержит только распознанные PHP структуры. Для SCM_RIGHTS проверьте число и тип полученных ресурсов, права доверенного Unix-peer, закройте лишние дескрипторы; не открывайте процессу произвольные права через общий socket-файл. Windows WSARecvMsg имеет собственные ограничения протокола; рабочие примеры здесь используют UDP. Ошибки преобразования и системного вызова дают warning/false; не полагайтесь на то, что все ошибки записаны именно в socket_last_error($socket): часть путей обновляет глобальную ошибку.

Связанные действия: socket_sendmsg, socket_cmsg_space, socket_recvfrom.

socket_cmsg_spaceНе угадывать размер буфера control для recvmsg.

PHP 5.5+ · sockets

socket_cmsg_space(int $level, int $type, int $num = 0): ?int

Передать открытую квитанцию доверенной Unix-стороне. Рассчитывается место ancillary-данных для одного дескриптора. Это не размер самого текста файла. SCM_RIGHTS передаёт доступ к уже открытому ресурсу.

socket_cmsg_space(SOL_SOCKET, SCM_RIGHTS, 1)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Проверен только синтаксис; окружение этого опыта не запускалось. Архив содержит полный код и указанные файлы. Ожидаемый результат относится к описанной ниже среде. Сначала прочитайте требования платформы и README, затем подготовьте отдельный учебный стенд.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать открытую квитанцию доверенной Unix-сторонеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/local-net.php';
if (PHP_OS_FAMILY === 'Windows' || !defined('SCM_RIGHTS')) { throw new RuntimeException('Нужен Unix со SCM_RIGHTS'); }
[$sender, $receiver] = localPair();
$file = fopen(__DIR__ . '/receipt.txt', 'rb');
if ($file === false) { throw new RuntimeException('Квитанция не открыта'); }
$message = ['iov'=>['F'], 'control'=>[['level'=>SOL_SOCKET, 'type'=>SCM_RIGHTS, 'data'=>[$file]]]];
if (socket_sendmsg($sender, $message, 0) !== 1) { throw new RuntimeException('Дескриптор не передан'); }
$received = ['buffer_size'=>1, 'controllen'=>socket_cmsg_space(SOL_SOCKET, SCM_RIGHTS, 1)];
if (socket_recvmsg($receiver, $received, 0) === false) { throw new RuntimeException('Нет сообщения'); }
$copy = $received['control'][0]['data'][0];
echo stream_get_contents($copy);
fclose($copy);
fclose($file);
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Квитанция: Молоток, 2 шт., 900 руб.

Приёмник читает реальную квитанцию из полученного дескриптора. Файл receipt.txt приложен; Windows-проверщик выполнил только lint, Unix-runtime не утверждается.

Ошибки и следующий шаг. Передавать дескриптор нужно только доверенному процессу: он получает доступ к ресурсу. Проверяйте level/type, число control-элементов и MSG_CTRUNC перед извлечением в общем коде. Sharedoffset/права семейства нельзя путать с копией содержимого.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Не угадывать размер буфера control для recvmsg.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
level / typeПоддержанная пара ancillary-протокола, не любая опция setsockopt.
numЧисло переменных элементов, например передаваемых fd; default 0.
РезультатРазмер с заголовком/выравниванием, зависящий от ОС/ABI.
ОшибкиPHP 8.5: ValueError для неподдержанной пары и недопустимого num.
Резерв для hop limit
  1. Выбраны IPPROTO_IPV6 и IPV6_HOPLIMIT
  2. PHP знает размер структуры
  3. CMSG_SPACE добавляет заголовок/выравнивание
  4. Положительный int пригоден для controllen

Этот вариант. Результат включает системные заголовки и выравнивание, а не только размер int. Число зависит от архитектуры; печатаем проверенное свойство, а не выдуманную переносимую константу.

Посчитать резерв для IPv6 hop limit без открытия сетиСамостоятельный пример
<?php
declare(strict_types=1);

$size = socket_cmsg_space(IPPROTO_IPV6, IPV6_HOPLIMIT);
echo is_int($size) && $size > 0 ? "Размер ancillary-буфера рассчитан\n" : "Нет размера\n";
Ожидаемый результат
Размер ancillary-буфера рассчитан

Этот вариант. Суммируются CMSG_SPACE отдельных сообщений. Это лишь подготовка массива, не включение соответствующих IPV6_RECV* опций и не получение пакета.

Сложить размеры двух различных control сообщенийСамостоятельный пример
<?php
declare(strict_types=1);

$limit = socket_cmsg_space(IPPROTO_IPV6, IPV6_HOPLIMIT);
$traffic = socket_cmsg_space(IPPROTO_IPV6, IPV6_TCLASS);
if (!is_int($limit) || !is_int($traffic)) throw new RuntimeException('Нет размеров');
$message = ['buffer_size' => 1024, 'controllen' => $limit + $traffic];
echo $message['controllen'] >= $limit ? "Резерв для двух control сообщений готов\n" : "Ошибка\n";
Ожидаемый результат
Резерв для двух control сообщений готов

level/type должны обозначать одну из структур, которые понимает PHP: SCM_RIGHTS/credentials либо перечисленные IPv6 ancillary-типы. num задаёт число элементов переменной части, прежде всего число дескрипторов SCM_RIGHTS; для одного fd передайте 1. Не подставляйте сюда число байтов обычного сообщения. В PHP 8.5 неизвестная пара, отрицательное/слишком большое num вызывает ValueError. Историческая сигнатура PHP.net допускает null; текущая реализация возвращает int при успехе и исключение при перечисленных ошибках.

Связанные действия: socket_recvmsg, socket_sendmsg.

socket_atmarkУзнать, стоит ли TCP поток на отметке urgent/OOB данных.

PHP 8.3+ · sockets

socket_atmark(Socket $socket): bool

Не спутать обычную команду с краем urgent-данных. На поддерживаемой платформе функция показывает urgent/OOB-позицию TCP. False может означать обычную позицию, поэтому ошибку дополнительно проверяют.

socket_atmark($peer)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Проверен только синтаксис; окружение этого опыта не запускалось. Архив содержит полный код и указанные файлы. Ожидаемый результат относится к описанной ниже среде. Сначала прочитайте требования платформы и README, затем подготовьте отдельный учебный стенд.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Не спутать обычную команду с краем urgent-данныхПример для указанного окружения
<?php
declare(strict_types=1);

if (!function_exists('socket_atmark')) { throw new RuntimeException('Нужна сборка со sockatmark'); }
require __DIR__ . '/local-net.php';
[$listener, $client, $peer] = localTcp();
writeBytes($client, "product:1\n");
socket_clear_error($peer);
$marked = socket_atmark($peer);
if ($marked === false && socket_last_error($peer) !== 0) { throw new RuntimeException('Не удалось проверить позицию'); }
if ($marked) { throw new RuntimeException('Этот протокол не использует urgent data'); }
serveProduct($peer);
showProduct(readCatalogLine($client));
socket_close($peer);
socket_close($client);
socket_close($listener);
Ожидаемый результат
Молоток — 8 шт.

Пример проверяет обычную позицию перед приёмом команды. Данные при этом не прочитаны самим atmark; чтение выполняется отдельно. На Windows этот вариант только lint.

Ошибки и следующий шаг. Доступность зависит от ОС/сборки PHP8.3+. Urgent TCP не универсальная приоритетная очередь; MSG_OOB/SO_OOBINLINE и различия ОС требуют специального протокола.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Узнать, стоит ли TCP поток на отметке urgent/OOB данных.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketОткрытый поддерживаемый потоковый Socket.
Результатtrue — позиция у urgent mark; false — не у mark либо системная ошибка.
ОшибкиWarning и false на системном отказе; заранее очистите и затем проверьте ошибку, если нужно различить false.
ДоступностьPHP 8.3+ только когда сборка предоставляет sockatmark; проверяйте function_exists.
Нет urgent сообщения
  1. Локальный TCP peer открыт
  2. Старая ошибка очищена
  3. atmark возвращает false
  4. Новая ошибка равна 0
  5. Это обычная позиция, не отказ

Этот вариант. Предусловие: PHP 8.3+ и наличие системного sockatmark, обычно Unix. Эта Windows-сборка проверяет синтаксис примера, а не обещает наличие функции. false при отсутствии метки — нормальный результат, а не всегда failure. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отличить отсутствие urgent mark от ошибкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
if (!function_exists('socket_atmark')) throw new RuntimeException('Нужна платформа с sockatmark');
[$listener, $client, $peer] = labTcp();
try {
    socket_clear_error($peer);
    $marked = socket_atmark($peer);
    if ($marked === false && socket_last_error($peer) !== 0) throw new RuntimeException('Ошибка atmark');
    echo $marked ? "Urgent mark\n" : "Обычная позиция потока\n";
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
Обычная позиция потока

Этот вариант. Проверка позиции не читает обычные байты. В протоколе с OOB отдельный байт читают MSG_OOB либо используют SO_OOBINLINE по правилам ОС. Не добавляйте urgent data как универсальную «приоритетную очередь»: TCP и системы различаются. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Проверить обычную позицию перед чтением командыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
if (!function_exists('socket_atmark')) throw new RuntimeException('Нужен sockatmark');
[$listener, $client, $peer] = labTcp();
try {
    labWriteAll($client, 'A42'); socket_clear_error($peer);
    $marked = socket_atmark($peer);
    if ($marked === false && socket_last_error($peer) !== 0) throw new RuntimeException('Ошибка atmark');
    if ($marked) throw new RuntimeException('Наш протокол не использует urgent data');
    echo labReadExact($peer, 3), "\n";
} finally { labClose($peer, $client, $listener); }
Ожидаемый результат
A42

Связанные действия: socket_recv, socket_set_option, socket_last_error.

socket_wsaprotocol_info_exportПодготовить описание Windows-сокета для конкретного процесса.

PHP 7.3+, Windows only · sockets

socket_wsaprotocol_info_export(Socket $socket, int $process_id): string|false

Передать данные через импортированный Windows-handle. Export создаёт описание под конкретный PID, import открывает ещё один handle общего endpoint, release освобождает mapping описания. Эти три действия имеют разные сроки жизни.

socket_wsaprotocol_info_export(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать данные через импортированный Windows-handleСамостоятельный пример
<?php
declare(strict_types=1);

if (PHP_OS_FAMILY !== 'Windows') { throw new RuntimeException('Нужна Windows'); }
require __DIR__ . '/local-net.php';
$receiver = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
$sender = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
if (!socket_bind($receiver, '127.0.0.1', 0) || !socket_bind($sender, '127.0.0.1', 0)) { throw new RuntimeException('UDP не привязан'); }
localTimeout($receiver);
localTimeout($sender);
socket_getsockname($receiver, $host, $port);
$id = socket_wsaprotocol_info_export($sender, getmypid());
if ($id === false) { throw new RuntimeException('Описание не создано'); }
$copy = socket_wsaprotocol_info_import($id);
if ($copy === false) { throw new RuntimeException('Описание не импортировано'); }
if (!socket_wsaprotocol_info_release($id)) { throw new RuntimeException('Mapping не освобождён'); }
$payload = "Молоток;8\n";
if (socket_sendto($copy, $payload, strlen($payload), 0, '127.0.0.1', $port) !== strlen($payload)) { throw new RuntimeException('Нет отправки'); }
if (socket_recvfrom($receiver, $data, 1024, 0, $from, $fromPort) === false) { throw new RuntimeException('Нет данных'); }
echo "Получена строка инвентаризации:\n", $data;
socket_close($copy);
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Получена строка инвентаризации:
Молоток;8

Учебный PID — текущий процесс:не нужен скрытый внешний worker. После release импортированный handle продолжает передавать данные; освобождение mapping не является закрытием сокета.

Ошибки и следующий шаг. Это Windows-only и демонстрация механизма, не проверка межпроцессного параллелизма. В рабочей паре ID передают только целевому доверенному процессу и release делают после подтверждённого import. Оба handle разделяют состояние, каждый владелец закрывает свой handle.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Подготовить описание Windows-сокета для конкретного процесса.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
socketЖивой endpoint, который надо разделить с процессом.
process_idPID процесса-импортёра. Описание пригодно только ему.
РезультатСтроковый ID mapping либо false с предупреждением Windows.
ДоступностьТолько Windows, PHP 7.3+; вход/выход Socket с PHP 8. На других системах самой функции нет.
Описание для конкретного PID
  1. Создан собственный UDP endpoint
  2. export использует getmypid для учебного случая
  3. export выполняет свою отдельную стадию
  4. Mapping и Socket освобождаются отдельно

Этот вариант. Windows-only: target PID взят у текущего процесса, чтобы самостоятельный пример не нуждался во внешнем worker. Это демонстрация механизма, не модель распараллеливания. Между процессами ID передают только предназначенному PID.

Разобрать Windows export в одном учебном процессеСамостоятельный пример
<?php
declare(strict_types=1);

if (PHP_OS_FAMILY !== 'Windows') throw new RuntimeException('Нужна Windows');
$original = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($original === false) throw new RuntimeException('Нет сокета');
$id = socket_wsaprotocol_info_export($original, getmypid());
if ($id === false) throw new RuntimeException('Не создано описание');
try { echo is_string($id) && $id !== '' ? "Описание для указанного PID создано\n" : "Ошибка\n"; }
finally { socket_wsaprotocol_info_release($id); socket_close($original); }
Ожидаемый результат
Описание для указанного PID создано

Этот вариант. После успешного import mapping можно освободить, но handle продолжает работать. Два handle относятся к одному underlying socket: они разделяют настройки и входящую очередь, а не создают независимые копии данных. Каждый владелец закрывает свой handle. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отделить mapping от времени жизни endpointСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
if (PHP_OS_FAMILY !== 'Windows') throw new RuntimeException('Нужна Windows');
[$receiver, $sender, $port] = labUdp();
$id = socket_wsaprotocol_info_export($sender, getmypid());
if ($id === false) throw new RuntimeException('Нет описания');
$copy = socket_wsaprotocol_info_import($id);
if ($copy === false) throw new RuntimeException('Нет handle');
try {
    if (!socket_wsaprotocol_info_release($id)) throw new RuntimeException('Не освобождено');
    socket_getsockname($sender, $host1, $port1); socket_getsockname($copy, $host2, $port2);
    echo $port1 === $port2 ? "Один исходящий endpoint\n" : "Разные порты\n";
    if (socket_sendto($copy, 'A42', 3, 0, '127.0.0.1', $port) !== 3) throw new RuntimeException('Нет отправки');
    if (socket_recvfrom($receiver, $data, 32, 0, $from, $fromPort) === false) throw new RuntimeException('Нет данных');
    echo $data, "\n";
} finally { labClose($copy, $sender, $receiver); }
Ожидаемый результат
Один исходящий endpoint
A42

В настоящей паре процессов родитель узнаёт PID доверенного ребёнка, экспортирует именно для него, передаёт ID по согласованному IPC и освобождает mapping после подтверждённого import. ID не является безопасным токеном аутентификации: имя mapping в текущей реализации предсказуемо. Не передавайте его посторонним и не принимайте произвольный ID из HTTP. Согласуйте одного читателя/писателя либо явный протокол владения: одновременные чтения конкурируют за одни данные. Close одного handle не заменяет освобождение mapping и не закрывает чужие handle.

Связанные действия: socket_wsaprotocol_info_import, socket_wsaprotocol_info_release, socket_close.

socket_wsaprotocol_info_importОткрыть собственный handle ранее экспортированного Windows endpoint.

PHP 7.3+, Windows only · sockets

socket_wsaprotocol_info_import(string $info_id): Socket|false

Передать данные через импортированный Windows-handle. Export создаёт описание под конкретный PID, import открывает ещё один handle общего endpoint, release освобождает mapping описания. Эти три действия имеют разные сроки жизни.

socket_wsaprotocol_info_import(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать данные через импортированный Windows-handleСамостоятельный пример
<?php
declare(strict_types=1);

if (PHP_OS_FAMILY !== 'Windows') { throw new RuntimeException('Нужна Windows'); }
require __DIR__ . '/local-net.php';
$receiver = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
$sender = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
if (!socket_bind($receiver, '127.0.0.1', 0) || !socket_bind($sender, '127.0.0.1', 0)) { throw new RuntimeException('UDP не привязан'); }
localTimeout($receiver);
localTimeout($sender);
socket_getsockname($receiver, $host, $port);
$id = socket_wsaprotocol_info_export($sender, getmypid());
if ($id === false) { throw new RuntimeException('Описание не создано'); }
$copy = socket_wsaprotocol_info_import($id);
if ($copy === false) { throw new RuntimeException('Описание не импортировано'); }
if (!socket_wsaprotocol_info_release($id)) { throw new RuntimeException('Mapping не освобождён'); }
$payload = "Молоток;8\n";
if (socket_sendto($copy, $payload, strlen($payload), 0, '127.0.0.1', $port) !== strlen($payload)) { throw new RuntimeException('Нет отправки'); }
if (socket_recvfrom($receiver, $data, 1024, 0, $from, $fromPort) === false) { throw new RuntimeException('Нет данных'); }
echo "Получена строка инвентаризации:\n", $data;
socket_close($copy);
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Получена строка инвентаризации:
Молоток;8

Учебный PID — текущий процесс:не нужен скрытый внешний worker. После release импортированный handle продолжает передавать данные; освобождение mapping не является закрытием сокета.

Ошибки и следующий шаг. Это Windows-only и демонстрация механизма, не проверка межпроцессного параллелизма. В рабочей паре ID передают только целевому доверенному процессу и release делают после подтверждённого import. Оба handle разделяют состояние, каждый владелец закрывает свой handle.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Открыть собственный handle ранее экспортированного Windows endpoint.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
info_idID экспортированного описания, созданного именно для текущего PID.
РезультатНовый Socket-handle общего endpoint либо warning/false.
ДоступностьТолько Windows, PHP 7.3+; вход/выход Socket с PHP 8. На других системах самой функции нет.
Описание для конкретного PID
  1. Создан собственный UDP endpoint
  2. export использует getmypid для учебного случая
  3. import выполняет свою отдельную стадию
  4. Mapping и Socket освобождаются отдельно

Этот вариант. Windows-only: target PID взят у текущего процесса, чтобы самостоятельный пример не нуждался во внешнем worker. Это демонстрация механизма, не модель распараллеливания. Между процессами ID передают только предназначенному PID.

Разобрать Windows import в одном учебном процессеСамостоятельный пример
<?php
declare(strict_types=1);

if (PHP_OS_FAMILY !== 'Windows') throw new RuntimeException('Нужна Windows');
$original = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($original === false) throw new RuntimeException('Нет сокета');
$id = socket_wsaprotocol_info_export($original, getmypid());
if ($id === false) throw new RuntimeException('Не создано описание');
$copy = socket_wsaprotocol_info_import($id);
try {
    if ($copy === false) throw new RuntimeException('Не импортировано');
    echo $copy instanceof Socket ? "Получен второй handle endpoint\n" : "Ошибка\n";
} finally {
    if ($copy !== false) socket_close($copy);
    socket_wsaprotocol_info_release($id); socket_close($original);
}
Ожидаемый результат
Получен второй handle endpoint

Этот вариант. После успешного import mapping можно освободить, но handle продолжает работать. Два handle относятся к одному underlying socket: они разделяют настройки и входящую очередь, а не создают независимые копии данных. Каждый владелец закрывает свой handle. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отделить mapping от времени жизни endpointСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
if (PHP_OS_FAMILY !== 'Windows') throw new RuntimeException('Нужна Windows');
[$receiver, $sender, $port] = labUdp();
$id = socket_wsaprotocol_info_export($sender, getmypid());
if ($id === false) throw new RuntimeException('Нет описания');
$copy = socket_wsaprotocol_info_import($id);
if ($copy === false) throw new RuntimeException('Нет handle');
try {
    if (!socket_wsaprotocol_info_release($id)) throw new RuntimeException('Не освобождено');
    socket_getsockname($sender, $host1, $port1); socket_getsockname($copy, $host2, $port2);
    echo $port1 === $port2 ? "Один исходящий endpoint\n" : "Разные порты\n";
    if (socket_sendto($copy, 'A42', 3, 0, '127.0.0.1', $port) !== 3) throw new RuntimeException('Нет отправки');
    if (socket_recvfrom($receiver, $data, 32, 0, $from, $fromPort) === false) throw new RuntimeException('Нет данных');
    echo $data, "\n";
} finally { labClose($copy, $sender, $receiver); }
Ожидаемый результат
Один исходящий endpoint
A42

В настоящей паре процессов родитель узнаёт PID доверенного ребёнка, экспортирует именно для него, передаёт ID по согласованному IPC и освобождает mapping после подтверждённого import. ID не является безопасным токеном аутентификации: имя mapping в текущей реализации предсказуемо. Не передавайте его посторонним и не принимайте произвольный ID из HTTP. Согласуйте одного читателя/писателя либо явный протокол владения: одновременные чтения конкурируют за одни данные. Close одного handle не заменяет освобождение mapping и не закрывает чужие handle.

Связанные действия: socket_wsaprotocol_info_export, socket_wsaprotocol_info_release, socket_close.

socket_wsaprotocol_info_releaseОсвободить mapping с описанием Windows-сокета, когда он больше не нужен.

PHP 7.3+, Windows only · sockets

socket_wsaprotocol_info_release(string $info_id): bool

Передать данные через импортированный Windows-handle. Export создаёт описание под конкретный PID, import открывает ещё один handle общего endpoint, release освобождает mapping описания. Эти три действия имеют разные сроки жизни.

socket_wsaprotocol_info_release(...)
  1. Приложенный local-net.php создаёт только собственный канал
  2. Запрос: product:1 с переводом строки либо явно указанная датаграмма
  3. Ответ содержит настоящие поля name=Молоток, stock=8
  4. Получатель проверяет формат и показывает название/остаток

Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.

Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.

Передать данные через импортированный Windows-handleСамостоятельный пример
<?php
declare(strict_types=1);

if (PHP_OS_FAMILY !== 'Windows') { throw new RuntimeException('Нужна Windows'); }
require __DIR__ . '/local-net.php';
$receiver = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
$sender = localSocket(AF_INET, SOCK_DGRAM, SOL_UDP);
if (!socket_bind($receiver, '127.0.0.1', 0) || !socket_bind($sender, '127.0.0.1', 0)) { throw new RuntimeException('UDP не привязан'); }
localTimeout($receiver);
localTimeout($sender);
socket_getsockname($receiver, $host, $port);
$id = socket_wsaprotocol_info_export($sender, getmypid());
if ($id === false) { throw new RuntimeException('Описание не создано'); }
$copy = socket_wsaprotocol_info_import($id);
if ($copy === false) { throw new RuntimeException('Описание не импортировано'); }
if (!socket_wsaprotocol_info_release($id)) { throw new RuntimeException('Mapping не освобождён'); }
$payload = "Молоток;8\n";
if (socket_sendto($copy, $payload, strlen($payload), 0, '127.0.0.1', $port) !== strlen($payload)) { throw new RuntimeException('Нет отправки'); }
if (socket_recvfrom($receiver, $data, 1024, 0, $from, $fromPort) === false) { throw new RuntimeException('Нет данных'); }
echo "Получена строка инвентаризации:\n", $data;
socket_close($copy);
socket_close($sender);
socket_close($receiver);
Ожидаемый результат
Получена строка инвентаризации:
Молоток;8

Учебный PID — текущий процесс:не нужен скрытый внешний worker. После release импортированный handle продолжает передавать данные; освобождение mapping не является закрытием сокета.

Ошибки и следующий шаг. Это Windows-only и демонстрация механизма, не проверка межпроцессного параллелизма. В рабочей паре ID передают только целевому доверенному процессу и release делают после подтверждённого import. Оба handle разделяют состояние, каждый владелец закрывает свой handle.

Следующая ситуация: получить реальный отказ отсутствующего товара и показать его человеку. В конце раздела с помощниками показан полный опыт product:99. Исправный канал может вернуть прикладную ошибку; это отдельный результат протокола.

Полный официальный контракт и дополнительные варианты. Далее сохранены все параметры, типы результатов, версии и ограничения этой операции. Сначала прочитайте пояснение соответствующего следующего примера.

Освободить mapping с описанием Windows-сокета, когда он больше не нужен.

Socket — объект одной конечной точки связи. Его получают функцией socket_create/accept, а затем передают первым аргументом следующей операции. В записи $ok = socket_bind($socket, '127.0.0.1', 0) переменная слева сохраняет результат, скобки содержат аргументы, а === false отличает отказ от нуля байтов или пустой строки. Все длины сетевых данных измеряются в байтах, не в буквах.

Начало: адрес, порт, TCP/UDP и первый локальный обмен. Вызов функции, передача результата через & и обработка исключений помогают прочитать синтаксис. В PHP 8 Socket/AddressInfo — объекты; старые проверки is_resource для них не подходят.

Часть договораОбъяснение
info_idID из export этого процесса; освобождается сохранённый shared-memory mapping.
Результатtrue при удалении известного mapping; false если его нет/уже освобождён. Endpoint не закрывается.
ДоступностьТолько Windows, PHP 7.3+; вход/выход Socket с PHP 8. На других системах самой функции нет.
Описание для конкретного PID
  1. Создан собственный UDP endpoint
  2. export использует getmypid для учебного случая
  3. release выполняет свою отдельную стадию
  4. Mapping и Socket освобождаются отдельно

Этот вариант. Windows-only: target PID взят у текущего процесса, чтобы самостоятельный пример не нуждался во внешнем worker. Это демонстрация механизма, не модель распараллеливания. Между процессами ID передают только предназначенному PID.

Разобрать Windows release в одном учебном процессеСамостоятельный пример
<?php
declare(strict_types=1);

if (PHP_OS_FAMILY !== 'Windows') throw new RuntimeException('Нужна Windows');
$original = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($original === false) throw new RuntimeException('Нет сокета');
$id = socket_wsaprotocol_info_export($original, getmypid());
if ($id === false) throw new RuntimeException('Не создано описание');
try {
    echo socket_wsaprotocol_info_release($id) ? "Mapping освобождён\n" : "Нет mapping\n";
    echo socket_wsaprotocol_info_release($id) ? "Неожиданное повторение\n" : "Повторно освобождать нечего\n";
} finally { socket_close($original); }
Ожидаемый результат
Mapping освобождён
Повторно освобождать нечего

Этот вариант. После успешного import mapping можно освободить, но handle продолжает работать. Два handle относятся к одному underlying socket: они разделяют настройки и входящую очередь, а не создают независимые копии данных. Каждый владелец закрывает свой handle. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.

Отделить mapping от времени жизни endpointСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/local-sockets.php';
if (PHP_OS_FAMILY !== 'Windows') throw new RuntimeException('Нужна Windows');
[$receiver, $sender, $port] = labUdp();
$id = socket_wsaprotocol_info_export($sender, getmypid());
if ($id === false) throw new RuntimeException('Нет описания');
$copy = socket_wsaprotocol_info_import($id);
if ($copy === false) throw new RuntimeException('Нет handle');
try {
    if (!socket_wsaprotocol_info_release($id)) throw new RuntimeException('Не освобождено');
    socket_getsockname($sender, $host1, $port1); socket_getsockname($copy, $host2, $port2);
    echo $port1 === $port2 ? "Один исходящий endpoint\n" : "Разные порты\n";
    if (socket_sendto($copy, 'A42', 3, 0, '127.0.0.1', $port) !== 3) throw new RuntimeException('Нет отправки');
    if (socket_recvfrom($receiver, $data, 32, 0, $from, $fromPort) === false) throw new RuntimeException('Нет данных');
    echo $data, "\n";
} finally { labClose($copy, $sender, $receiver); }
Ожидаемый результат
Один исходящий endpoint
A42

В настоящей паре процессов родитель узнаёт PID доверенного ребёнка, экспортирует именно для него, передаёт ID по согласованному IPC и освобождает mapping после подтверждённого import. ID не является безопасным токеном аутентификации: имя mapping в текущей реализации предсказуемо. Не передавайте его посторонним и не принимайте произвольный ID из HTTP. Согласуйте одного читателя/писателя либо явный протокол владения: одновременные чтения конкурируют за одни данные. Close одного handle не заменяет освобождение mapping и не закрывает чужие handle.

Связанные действия: socket_wsaprotocol_info_export, socket_wsaprotocol_info_import, socket_close.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.