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

Дерево руководства
Глава 120 · От основ к системному API
Sockets: собственный TCP/UDP протокол и полный API
От первого локального endpoint до всех функций, AddressInfo, Windows IPC, ancillary data и платформенных констант.
Начать с байтов, затем договориться о сообщении
Команда A42 проходит через два отдельных endpoint
Локальная лаборатория выполняет обе роли в одном процессе, но Socket клиента, listener и принятый peer остаются разными объектами.
- 1Listener: 127.0.0.1:0↓
bind выбирает свободный порт, listen включает очередь. Listener принимает соединения и не читает A42.
- 2Клиент → выбранный порт↓
connect устанавливает TCP-связь. accept возвращает отдельный серверный peer.
- 3A42: длина 3 байта↓
socket_write передаёт байты клиенту ОС. Несколько чтений могут понадобиться даже после одной записи.
- 4Peer собирает A + 42↓
Цикл дочитывает до согласованной длины. TCP сохраняет порядок байтов, но не границы вызовов write.
- 5Ответ accepted:A42:2
Приложение проверяет команду и отправляет отдельное подтверждение. Сам успех write ещё не означает выполненный заказ.
Схема показывает роли и порядок действий, а не устройство пакетов в ОС. Loopback остаётся на этой машине; UDP требует отдельного договора о потере, повторе и порядке датаграмм.
Хотим запросить остаток без HTTP, передав собственную короткую команду product:1
. Сокет переносит байты, но не знает ни товаров, ни JSON, ни конца нашего сообщения. Поэтому участники договариваются: команда и ответ заканчиваются LF, ответ содержит JSON, размер каждой строки не больше 4096 байт. Для обычного веб-API удобнее cURL; прямые сокеты нужны, когда вы управляете протоколом.
- Клиент: product:1 + LF
- Обработчик: выбрать name=Молоток, stock=8
- Ответ: {"name":"Молоток","stock":8} + LF
- Клиент: 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 секунды. |
| localListener | create → 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 и peer | Listener принимает новые TCP соединения; accept возвращает отдельный peer для обмена с конкретным клиентом. |
| TCP | Надёжный упорядоченный поток байтов. Одна отправка может читаться частями, несколько отправок — вместе. |
| UDP | Отдельные датаграммы. Возможны потеря, дублирование, изменение порядка; успешный send не доказывает получение. |
| Blocking | Вызов ждёт данные/свободный буфер; таймаут и общий срок задают осознанно. |
| AddressInfo | Описание найденного адреса, из которого позднее можно создать Socket. Это ещё не соединение. |
- Клиент имеет свой Socket
- Сервер слушает только 127.0.0.1:свободный_порт
- accept создаёт отдельный серверный peer
- Клиент пишет байты A42
- 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).
<?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.
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 дочитывает до трёх байтов: даже короткое сообщение поток не обязан вернуть одним чтением. Для произвольной длинной записи аналогичный цикл нужен также отправителю.
<?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 с двухсекундными таймаутами чтения/записи. |
| labListener | TCP 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.
<?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 содержит sku=A42 и qty=2
- strlen считает байты JSON
- pack N делает заголовок длины
- Получатель дочитывает 4 байта
- Проверяет предел и дочитывает тело
- json_decode проверяет формат, приложение — поля
Этот вариант. Проверки различают транспорт, структуру JSON и смысл команды. Внутри кадра нельзя использовать unserialize для недоверенных объектов. Даже этот ограниченный пример не добавляет TLS/аутентификацию и не является публичным endpoint. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 не означает сетевой сбой.
| Случай | Реакция |
|---|---|
| Создание вернуло false | Socket ещё нет: 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, тип/число всех присланных ресурсов и закройте лишние.
<?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_DIVERT | FreeBSD divert: получение пакетов, перенаправленных firewall. Низкоуровневый привилегированный интерфейс, не обычный TCP listener; наличие константы не обещает поддержку всеми обёртками PHP. С PHP 8.3.0. |
AF_PACKET | Linux уровень канала (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_DGRAM | NetBSD connection-oriented datagram type; тип требует поддержанного протокола, не аналог универсального UDP connect. С PHP 8.4.0. |
SOCK_DCCP | Datagram 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_FILTER | Illumos/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_LOCAL | BSD уровень LOCAL_* параметров локальных sockets; отличается от SOL_SOCKET. |
SOMAXCONN | Системная граница/default запроса backlog; ядро может ограничить дополнительно. С PHP 8.4 default create_listen, но не default socket_listen (0). |
SHUT_RD | socket_shutdown mode: прекратить дальнейший приём; не освобождает объект. PHP 8.5, числовое значение соответствует 0. С PHP 8.5.0. |
SHUT_WR | socket_shutdown mode: завершить отправку, сохранив возможность чтения; PHP 8.5, соответствует 1. С PHP 8.5.0. |
SHUT_RDWR | socket_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_ADDRCONFIG | Resolver flags: выбирать семейства по конфигурации адресов машины; loopback может не считаться достаточной настройкой ОС. |
AI_ALL | Вместе с AI_V4MAPPED запрашивать и IPv6, и IPv4-mapped варианты согласно resolver ОС. |
AI_CANONIDN | Resolver flags: преобразовывать canonical name из IDN-представления на поддержанной системе; зависит от locale/resolver. |
AI_CANONNAME | Запросить canonical name в первом результате, если resolver его предоставляет. Это не доказательство доверия узлу. |
AI_IDN | Разрешить resolver преобразовать международное имя по правилам locale/IDN поддержанной системы; не переносимый универсальный UTF-8 валидатор. |
AI_NUMERICHOST | Запретить DNS/поиск имён для host: требуется числовой IP адрес. |
AI_NUMERICSERV | service должен быть числовой строкой порта, без поиска имени службы. |
AI_PASSIVE | Resolver 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_ZEROCOPY | Linux запрос zero-copy отправки после SO_ZEROCOPY. Требует контроля completion через error queue и времени жизни памяти; наличие PHP-константы не доказывает безопасный zero-copy цикл PHP-строк. С PHP 8.2.0. |
MSG_CMSG_CLOEXEC | Linux recvmsg flags: close-on-exec для полученных SCM_RIGHTS descriptors. Не закрывает их после обычного PHP вызова. |
MSG_CONFIRM | Linux send flags: подтвердить достижимость соседа для neighbour discovery; не прикладной ACK. |
MSG_CTRUNC | Выходной recvmsg flag: ancillary control обрезан; данных/дескрипторов может не хватать для безопасной обработки. |
MSG_DONTWAIT | Неблокирующий режим конкретного send/recv вызова, если поддержан. Отличается от постоянного set_nonblock. |
MSG_ERRQUEUE | Linux recvmsg flags: читать асинхронную error queue. PHP не декодирует любую native control-структуру этой очереди автоматически. |
MSG_MORE | Linux send flags: есть ещё данные для объединения; эффект TCP/UDP различается. Не заменяет framing и может задержать отправку. |
MSG_NOSIGNAL | Linux/поддержанные Unix send flags: не посылать SIGPIPE при отказе записи; ошибка EPIPE всё равно проверяется. |
MSG_TRUNC | Выходной recvmsg flag усечения datagram; как входной флаг в некоторых ОС меняет возвращаемую длину. Возвращённое число тогда может превышать реально скопированные байты. |
MSG_WAITFORONE | Linux recvmmsg flag: после первого сообщения не блокировать последующие. В PHP sockets нет обёртки recvmmsg; наличие имени не добавляет её к recvmsg. |
Общие SO_* и TCP настройки
Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.
| Константа (int) | Роль, значение и ограничение |
|---|---|
SO_DEBUG | SOL_SOCKET, int 0/1: системная диагностика сокета; зависит от прав/реализации. Microsoft providers обычно не выводят диагностику. |
SO_REUSEADDR | SOL_SOCKET, int 0/1 до bind: разрешить определённые случаи повторного использования адреса. Семантика Unix/Windows различается; не универсальная безопасная настройка любого сервера. |
SO_REUSEPORT | SOL_SOCKET, int 0/1 до bind: разрешить совместное использование порта по правилам ОС. Linux/macOS/BSD; Windows не предоставляет. Балансировка и требования к UID различаются. |
SO_KEEPALIVE | SOL_SOCKET, int 0/1: включить TCP keepalive-пробы. Интервалы задаёт ОС/специальные TCP_* опции; не заменяет прикладной ping/deadline. |
SO_DONTROUTE | SOL_SOCKET, int 0/1: ограничить маршрутизацию исходящих пакетов. Реальный эффект зависит от провайдера; это не ACL. |
SO_LINGER | SOL_SOCKET, массив l_onoff/l_linger: политика ожидания данных при close. Включённое значение с нулевым сроком способно вызвать TCP reset; не применять без понимания потери данных. |
SO_BROADCAST | SOL_SOCKET, int 0/1: разрешение отправки IPv4 broadcast datagrams. Не делает unicast адрес broadcast и не включает multicast. |
SO_OOBINLINE | SOL_SOCKET, int 0/1: доставлять urgent данные в обычном входном потоке; меняет способ чтения MSG_OOB/atmark. |
SO_SNDBUF | SOL_SOCKET, int байтов: размер буфера отправки. ОС ограничивает/корректирует; проверяйте get_option, не предполагайте точное равенство. |
SO_RCVBUF | SOL_SOCKET, int байтов: буфер приёма. Не максимальный размер вашего PHP сообщения и не автоматическое ограничение длины команды. |
SO_SNDLOWAT | SOL_SOCKET, int байтов: low-water отметка отправки; поддержка изменения ограничена. Windows TCP/IP не поддерживает эту опцию. |
SO_RCVLOWAT | SOL_SOCKET, int байтов: low-water отметка приёма/готовности по правилам ОС. Windows TCP/IP не поддерживает. |
SO_SNDTIMEO | SOL_SOCKET, массив sec/usec: предел блокирующей отправки. Windows округляет до миллисекунд; не общий срок всех записей. |
SO_RCVTIMEO | SOL_SOCKET, массив sec/usec: предел блокирующего чтения; ноль обычно снимает его. Не таймаут DNS/всей сессии. |
SO_TYPE | SOL_SOCKET, чтение int SOCK_*: тип уже созданного endpoint. Не переключается записью. |
SO_ERROR | SOL_SOCKET, чтение int: отложенная ошибка ядра с очисткой по системному договору. Проверяют после non-blocking connect; отличается от сохранённого socket_last_error PHP. |
SO_ZEROCOPY | Linux SOL_SOCKET, int 0/1: разрешить zero-copy механизм. Требует полноценной поддержки completion/буферов, а не только set_option true. С PHP 8.2.0. |
TCP_NODELAY | SOL_TCP, int 0/1: отключить Nagle для мелких записей. Не гарантирует одно write = один пакет/ответ. |
TCP_KEEPCNT | SOL_TCP, int: число неотвеченных keepalive проб перед признанием соединения неисправным; поддержка/границы ОС. С PHP 8.2.0. |
TCP_KEEPIDLE | SOL_TCP, int секунд: простой до первой keepalive пробы; нужен SO_KEEPALIVE. С PHP 8.2.0. |
TCP_KEEPINTVL | SOL_TCP, int секунд: интервал между keepalive пробами; не частота сообщений приложения. С PHP 8.2.0. |
TCP_KEEPALIVE | Платформенное SOL_TCP имя времени простоя keepalive (в частности macOS), int секунд. Не путать с общим флагом SO_KEEPALIVE. С PHP 8.2.0. |
TCP_NOTSENT_LOWAT | Linux SOL_TCP, int байтов: порог ещё не переданных TCP байтов для готовности/буферизации. Не лимит всего ответа. С PHP 8.2.0. |
SO_MARK | Linux SOL_SOCKET, int: mark пакетов для политики маршрутизации/фильтрации; нужны соответствующие capabilities. С PHP 8.1.0. |
SO_USER_COOKIE | FreeBSD SOL_SOCKET, int: пользовательская метка пакетов endpoint для системных механизмов. Не HTTP cookie. С PHP 8.1.0. |
SO_RTABLE | OpenBSD SOL_SOCKET, int: таблица маршрутизации endpoint; допускаемые значения/права задаёт ОС. С PHP 8.2.0. |
SO_ACCEPTFILTER | FreeBSD/NetBSD SOL_SOCKET: строка имени установленного accept filter. Задерживает готовность accept до условия ядра; не PHP callback. Имя ограничено системным полем (обычно 15 байтов). С PHP 8.1.0. |
SO_DONTTRUNC | macOS SOL_SOCKET, int 0/1: изменить поведение обработки длинных сообщений/усечения. Согласуйте с буфером и системным договором; не переносимо. С PHP 8.1.0. |
SO_WANTMORE | macOS SOL_SOCKET, int 0/1: расширенное уведомление о наличии данных сверх текущего запроса. Не переносимый способ узнать длину TCP сообщения. С PHP 8.1.0. |
SO_INCOMING_CPU | Linux SOL_SOCKET, int: CPU, связанный с входящим потоком; тюнинг привязки обработки, не номер PHP worker. С PHP 8.2.0. |
SO_MEMINFO | Linux SOL_SOCKET: native массив счётчиков памяти endpoint. Наличие имени не означает полноценного array-конвертера этой структуры в PHP get_option; не считайте общий int полным отчётом. С PHP 8.2.0. |
SO_BPF_EXTENSIONS | Linux SOL_SOCKET, чтение int: уровень поддержанных classic BPF расширений. Это capability ядра, не загрузка программы фильтра. С PHP 8.2.0. |
SO_SETFIB | FreeBSD SOL_SOCKET, int: номер FIB/таблицы маршрутизации. Нужен заранее настроенный системный routing context. С PHP 8.2.0. |
SO_ATTACH_REUSEPORT_CBPF | Linux SOL_SOCKET: native sock_fprog выбирает endpoint в reuseport-группе. PHP не предоставляет произвольный конструктор этой C-структуры через обычный int; наличие константы не делает пример C готовым PHP-кодом. С PHP 8.3.0. |
SO_DETACH_BPF | Linux SOL_SOCKET: снять ранее прикреплённый фильтр; синоним системного SO_DETACH_FILTER. Операция может требовать права. С PHP 8.3.0. |
SO_DETACH_FILTER | Linux SOL_SOCKET: удалить фильтр endpoint; не путать с закрытием самого Socket. С PHP 8.3.0. |
SO_RERROR | NetBSD SOL_SOCKET, int 0/1: уведомлять о переполнении входного буфера. Не общий переключатель PHP исключений. С PHP 8.3.0. |
SO_ZEROIZE | OpenBSD SOL_SOCKET, int 0/1: обнулять освобождаемые socket buffers. Не гарантирует очистку всех копий секретной PHP-строки. С PHP 8.3.0. |
SO_SPLICE | OpenBSD SOL_SOCKET: native структура соединения двух endpoints в ядре. PHP универсальный set_option не строит такую структуру автоматически; не равнозначно stream_copy_to_stream. С PHP 8.3.0. |
SO_REUSEPORT_LB | FreeBSD SOL_SOCKET, int 0/1: reuse-port с распределением нагрузки. Настраивается согласованно до bind, не переносимо в Windows. С PHP 8.3.0. |
TCP_DEFER_ACCEPT | Linux SOL_TCP, int секунд: пробуждать listener при поступлении данных, с системными ограничениями. Не заменяет обработку таймаута клиента. С PHP 8.1.0. |
TCP_CONGESTION | Linux SOL_TCP: строка имени доступного алгоритма congestion control при set. PHP getter общего int не следует принимать за полноценный строковый getsockopt этого native параметра. С PHP 8.2.0. |
TCP_QUICKACK | Linux SOL_TCP, int 0/1: временно изменить delayed ACK поведение. Стек может затем сменить режим; не постоянная гарантия задержки. С PHP 8.3.0. |
TCP_REPAIR | Linux SOL_TCP, int: режим восстановления TCP состояния, предназначенный для специальных системных инструментов и прав, не обычного приложения. С PHP 8.3.0. |
TCP_SYNCNT | Linux SOL_TCP, int: число повторных SYN до прекращения подключения; не точное число секунд connect timeout. С PHP 8.4.0. |
SO_BINDTODEVICE | Linux SOL_SOCKET, строка: ограничить endpoint сетевым устройством, например lo; требуются права, не переносимо в Windows. |
SO_FAMILY | Платформенное SOL_SOCKET чтение семейства endpoint. Не менять семейство существующего Socket записью. |
SO_LABEL | BSD/macOS security label endpoint: native сложная структура/политика MAC. PHP generic int не является полноценным преобразованием label. |
SO_LISTENQLEN | FreeBSD SOL_SOCKET чтение int: текущая длина очереди listener, не количество всех клиентов приложения. |
SO_LISTENQLIMIT | FreeBSD SOL_SOCKET чтение int: установленный предел очереди listener. |
SO_PASSCRED | Linux SOL_SOCKET, int 0/1: включить доставку credentials ancillary у Unix sockets; используйте recvmsg и проверку доверия peer. |
SO_PEERLABEL | BSD/macOS security label peer: native MAC структура; наличие константы не означает автоматическую декодировку label в PHP. |
SO_EXCLUSIVEADDRUSE | Windows SOL_SOCKET, int 0/1 до bind: запретить принудительное повторное связывание адреса/порта другим socket. С PHP 8.4.0. |
SO_EXCLBIND | Solaris SOL_SOCKET, int 0/1: эксклюзивное связывание адреса по системным правилам. С PHP 8.4.0. |
SO_NOSIGPIPE | macOS/FreeBSD SOL_SOCKET, int 0/1: подавить SIGPIPE при записи; ошибка операции остаётся, её надо обработать. С PHP 8.4.0. |
SO_LINGER_SEC | macOS SOL_SOCKET: вариант linger с единицей секунды вместо тиков. PHP-конвертер зависит от option: не переносите автоматически массив SO_LINGER без проверки поддержки конкретной сборки. С PHP 8.4.0. |
SO_BINDTOIFINDEX | SOL_SOCKET, int: индекс интерфейса вместо его имени; права и поддержка ОС обязательны. С PHP 8.4.0. |
SO_BUSY_POLL | Linux SOL_SOCKET, int микросекунд: короткое busy polling при блокирующем приёме. Уменьшение задержки оплачивается CPU; нужны условия драйвера/прав. С PHP 8.5.0. |
TCP_FUNCTION_BLK | FreeBSD SOL_TCP: set строкой имени TCP stack/function block; get массив function_set_name/pcbcnt. PHP 8.5 проверяет длину/нулевые байты имени. С PHP 8.5.0. |
TCP_FUNCTION_ALIAS | FreeBSD SOL_TCP чтение массива function_set_name/pcbcnt для alias выбранного TCP stack. Не пользовательский PHP callback. С PHP 8.5.0. |
TCP_REUSPORT_LB_NUMA | FreeBSD 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_ALGORITHM | FreeBSD SOL_TCP идентификатор BBR алгоритма. Конкретные значения/применимость определяет установленный TCP stack; не универсальный Linux переключатель BBR. С PHP 8.5.0. |
IP, multicast и UDP-Lite
Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.
| Константа (int) | Роль, значение и ограничение |
|---|---|
UDPLITE_RECV_CSCOV | SOL_UDPLITE, int байтов: минимальный охват checksum входящих датаграмм; 0 требует полный охват, положительный минимум 8 (заголовок). С PHP 8.3.0. |
UDPLITE_SEND_CSCOV | SOL_UDPLITE, int байтов: охват checksum при отправке; 0 — весь пакет, значение >=8 задаёт частичный охват. Поддержка протокола обязательна. С PHP 8.3.0. |
IP_DONTFRAG | FreeBSD IPPROTO_IP, int 0/1: запрет IPv4 фрагментации исходящего пакета; слишком большой пакет может дать ошибку. С PHP 8.3.0. |
IP_MTU_DISCOVER | Linux 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_PORT | Linux IPPROTO_IP, int 0/1 до bind: не резервировать ephemeral port при bind(address,0), выбрать его позже при connect. С PHP 8.3.0. |
IPV6_HOPLIMIT | IPv6 ancillary type: int hop limit конкретного пакета; приём запрашивают IPV6_RECVHOPLIMIT. |
IPV6_MULTICAST_HOPS | IPPROTO_IPV6, int: hop limit multicast, -1 выбирает default, допустимый явный предел 0…255. |
IPV6_MULTICAST_IF | IPPROTO_IPV6: выходной multicast интерфейс, в PHP индекс/имя; 0 выбирает маршрут ОС. |
IPV6_MULTICAST_LOOP | IPPROTO_IPV6, int 0/1: локальный multicast loopback; детали влияния sender/receiver зависят от ОС. |
IPV6_PKTINFO | IPv6 ancillary type: addr/ifindex для адреса/интерфейса пакета. Чтобы получать, включают IPV6_RECVPKTINFO. |
IPV6_RECVHOPLIMIT | IPPROTO_IPV6, int 0/1: включить получение ancillary IPV6_HOPLIMIT через recvmsg. |
IPV6_RECVPKTINFO | IPPROTO_IPV6, int 0/1: получать ancillary IPV6_PKTINFO с адресом и интерфейсом. |
IPV6_RECVTCLASS | IPPROTO_IPV6, int 0/1: получать ancillary IPV6_TCLASS. |
IPV6_TCLASS | IPPROTO_IPV6/ancillary, int: IPv6 traffic class; не абсолютный приоритет доставки приложения. |
IPV6_UNICAST_HOPS | IPPROTO_IPV6, int: hop limit unicast, -1 — default, 0…255 — явное значение. |
IPV6_V6ONLY | IPPROTO_IPV6, int 0/1 обычно до bind: IPv6-only либо разрешение IPv4-mapped по возможностям ОС. Default различается между системами. |
IP_MULTICAST_IF | IPPROTO_IP: выходной multicast интерфейс. PHP принимает индекс/имя, а не C-структуру адреса; 0 — выбор ОС. |
IP_MULTICAST_LOOP | IPPROTO_IP, int 0/1: локальный multicast loopback. Winsock применяет настройку на пути приёма, BSD-подобные стеки — на пути отправки; это различие нельзя менять местами. |
IP_MULTICAST_TTL | IPPROTO_IP, int 0…255: TTL IPv4 multicast; обычный default 1, область зависит от маршрутизации сети. |
IP_PORTRANGE | BSD 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_SOURCE | Set-only IPPROTO_IP/IPV6: запретить source в уже принятой group; массив group/interface/source. |
MCAST_JOIN_GROUP | Set-only IPPROTO_IP/IPV6: присоединиться к group на interface, получая разрешённые источники. Массив group/interface; не запускает внешнюю отправку. |
MCAST_JOIN_SOURCE_GROUP | Set-only: присоединиться к group только для указанного source. Массив group/interface/source. |
MCAST_LEAVE_GROUP | Set-only: покинуть group на interface; не закрывает Socket. Требуется корректная структура и существующее членство по правилам ОС. |
MCAST_LEAVE_SOURCE_GROUP | Set-only: снять подписку group/source на interface. |
MCAST_UNBLOCK_SOURCE | Set-only: снять прежнюю блокировку source в group/interface. |
UDP_SEGMENT | Linux SOL_UDP: размер полезной части сегмента GSO в байтах, native uint16. PHP 8.5 поддерживает этот размер; требуется поддержка ядра/стека, не обычная нарезка любой строки на независимые команды. С PHP 8.5.0. |
IP_BINDANY | FreeBSD/NetBSD/OpenBSD IPPROTO_IP, int 0/1: разрешить bind нелокального адреса, с системными правами. Не даёт маршрут и не делает удалённый адрес собственным. С PHP 8.5.0. |
Ancillary credentials, descriptor и filters
Справочник имён: смысл и ограничения приведены для каждого значения. Наличие проверяйте перед обращением к платформенной константе.
| Константа (int) | Роль, значение и ограничение |
|---|---|
SCM_RIGHTS | SOL_SOCKET control type: передача открытых дескрипторов через Unix socket. data — непустой список Socket/stream, нужен обычный байт; не поддержано Windows. |
SCM_CREDENTIALS | SOL_SOCKET control type с pid/uid/gid процесса, поддержка/проверка ядром ОС. Не доверяйте произвольному JSON с такими ключами. |
SCM_CREDS | Платформенное credentials control type (BSD). Его native структура отличается от Linux SCM_CREDENTIALS. С PHP 8.2.0. |
SCM_CREDS2 | FreeBSD расширенный credentials control type для постоянной передачи учётных данных. Используется вместе с LOCAL_CREDS_PERSISTENT по договору ОС. С PHP 8.2.0. |
LOCAL_CREDS | SOL_LOCAL, int 0/1: включить credentials ancillary для локальных сообщений на поддержанной BSD системе. С PHP 8.2.0. |
LOCAL_CREDS_PERSISTENT | SOL_LOCAL, int 0/1: запрашивать credentials не только один раз, но постоянно; FreeBSD, структуры SCM_CREDS2. С PHP 8.2.0. |
SKF_AD_OFF | Linux classic BPF: базовое отрицательное смещение для ancillary load, а не обычный byte offset пакета. Используется в native BPF инструкции. С PHP 8.2.0. |
SKF_AD_PROTOCOL | Linux BPF ancillary selector: Ethernet protocol пакета. С PHP 8.2.0. |
SKF_AD_PKTTYPE | Linux BPF ancillary selector: тип пакета относительно локального узла (host/broadcast/multicast и др.). С PHP 8.2.0. |
SKF_AD_IFINDEX | Linux BPF ancillary selector: индекс сетевого интерфейса пакета. С PHP 8.2.0. |
SKF_AD_NLATTR | Linux BPF extension: поиск netlink attribute в данных сообщения. С PHP 8.2.0. |
SKF_AD_NLATTR_NEST | Linux BPF extension: поиск вложенного netlink attribute. С PHP 8.2.0. |
SKF_AD_MARK | Linux BPF ancillary selector: packet mark, не пользовательская строковая метка. С PHP 8.2.0. |
SKF_AD_QUEUE | Linux BPF ancillary selector: mapping аппаратной/системной очереди пакета. С PHP 8.2.0. |
SKF_AD_HATYPE | Linux BPF ancillary selector: тип hardware link устройства. С PHP 8.2.0. |
SKF_AD_RXHASH | Linux BPF ancillary selector: receive hash потока, вычисленный стеком/устройством. С PHP 8.2.0. |
SKF_AD_CPU | Linux BPF ancillary selector: текущий CPU обработки пакета. С PHP 8.2.0. |
SKF_AD_ALU_XOR_X | Linux classic BPF extension для операции XOR аккумулятора с регистром X. С PHP 8.2.0. |
SKF_AD_VLAN_TAG | Linux BPF ancillary selector: VLAN tag, сохранённый в metadata. С PHP 8.2.0. |
SKF_AD_VLAN_TAG_PRESENT | Linux BPF ancillary selector: признак наличия VLAN tag; нулевой tag не эквивалентен отсутствию. С PHP 8.2.0. |
SKF_AD_PAY_OFFSET | Linux BPF ancillary selector: вычисленное смещение полезной нагрузки пакета. С PHP 8.2.0. |
SKF_AD_RANDOM | Linux BPF ancillary selector: системное псевдослучайное значение для обработки пакета, не источник токенов PHP. С PHP 8.2.0. |
SKF_AD_VLAN_TPID | Linux BPF ancillary selector: тип протокола VLAN tag. С PHP 8.2.0. |
SKF_AD_MAX | Граница диапазона Linux BPF ancillary extensions, а не самостоятельное поле пакета. С PHP 8.2.0. |
FIL_ATTACH | SOL_FILTER на Illumos/Solaris: присоединить named socket filter. Нужен установленный системный фильтр и соответствующий native договор. |
FIL_DETACH | SOL_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_ECONNRESET | Peer сбросил соединение; прежнюю сессию продолжать нельзя. |
SOCKET_ENOBUFS | Ядру не хватает буферов/сетевых ресурсов. |
SOCKET_EISCONN | Endpoint уже соединён, а операция требует другого состояния. |
SOCKET_ENOTCONN | Endpoint ещё не соединён/уже разъединён. |
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_EPROCLIM | Windows: предел процессов/задач провайдера sockets. |
SOCKET_EDUOT | Опечатка текущего списка PHP.net; корректное имя SOCKET_EDQUOT (превышена квота). |
SOCKET_ESTALE | Устаревшая ссылка/handle на объект системного хранилища. |
SOCKET_EDISCON | Windows: корректное завершение соединения, сообщённое провайдером. |
SOCKET_SYSNOTREADY | Windows: сетевая подсистема ещё не готова. |
SOCKET_VERNOTSUPPORTED | Windows: нужная версия Winsock не поддержана. |
SOCKET_NOTINITIALISED | Windows: Winsock не инициализирован; обычно это проблема среды/расширения, не PHP connect-параметра. |
SOCKET_HOST_NOT_FOUND | Resolver Windows не нашёл узел; не переносите автоматически на false socket_addrinfo_lookup. |
SOCKET_TRY_AGAIN | Временный отказ resolver Windows; повтор допустим только с ограничением срока/попыток. |
SOCKET_NO_RECOVERY | Resolver Windows сообщает невосстановимый отказ. |
SOCKET_NO_DATA | Resolver нашёл имя, но нет запрошенных данных/записи. |
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_EBADFD | Descriptor существует, но находится в недопустимом состоянии. |
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 передаётся следующим операциям.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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() без объекта нужен, когда объекта ещё нет. |
- AF_INET → IPv4
- SOCK_STREAM → поток
- SOL_TCP → TCP
- Socket создан, адрес ещё не назначен
- socket_close освобождает его
Этот вариант. AF_INET выбирает семейство IPv4, SOCK_STREAM — упорядоченный поток байтов, SOL_TCP — TCP. Создание endpoint само не делает его сервером и не подключает к адресу. instanceof проверяет класс, socket_close освобождает 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 сохраняет границы сообщений, но не гарантирует доставку/порядок. Создание не отправило показание.
<?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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
| port | 0…65535 для IP. 0 — свободный порт ОС; не число клиентов. |
| Результат | true либо false с warning; номер ошибки у этого socket. |
| Версии | PHP 8: объект; PHP 8.5: ValueError для порта вне диапазона. |
- Создан UDP endpoint
- bind 127.0.0.1:0
- ОС выбирает свободный порт
- 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-ввода.
<?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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 для них не подходят.
| Часть договора | Объяснение |
|---|---|
| socket | Socket после create + bind либо addrinfo_bind. |
| backlog | Желаемая длина очереди ожидающих соединений; ОС может ограничить. Default этой функции 0, в отличие от create_listen. |
| Результат | true — слушающий режим включён; false + warning — отказ. Это ещё не принятый клиент. |
- create → TCP endpoint
- bind → 127.0.0.1 и свободный порт
- listen(2) → очередь ожидания
- accept позже вернёт отдельный клиентский Socket
Этот вариант. listen возвращает управление сразу: он включает пассивный режим, а не выполняет бесконечный цикл. backlog=2 относится к очереди ещё не принятых соединений.
<?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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
- Listener уже слушает localhost
- Клиент соединяется
- accept создаёт отдельный $peer
- A42 читается из $peer
- 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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
| address | IPv4, IPv6 без URL-скобок либо путь AF_UNIX. Не HTTP URL. |
| port | Порт удалённой стороны, обязателен для IPv4/IPv6. Nullable с PHP 8 для иных семейств. |
| Результат | true либо false. Для non-blocking false + pending-код отличается от окончательного отказа. |
- getsockname сообщает адрес listener
- socket_connect использует этот адрес
- accept получает второй конец
- Сервис посылает ready
- Клиент читает ровно 6 байтов
Этот вариант. Порт не задан заранее: он прочитан у собственного listener. connect устанавливает TCP-соединение, но не доказывает готовность прикладного протокола. Строка ready — отдельный договор нашего учебного сервиса. Приложенный 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 {
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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 элементы — объекты, прежде ресурсы. |
- Передан пустой $pair
- create_pair заполняет индексы 0 и 1
- Конец 0 пишет OK
- Конец 1 читает OK
- Закрыть нужно оба конца
Этот вариант. &$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 как адрес.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Проверен только синтаксис; окружение этого опыта не запускалось. Архив содержит полный код и указанные файлы. Ожидаемый результат относится к описанной ниже среде. Сначала прочитайте требования платформы и 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 для них не подходят.
| Часть договора | Объяснение |
|---|---|
| port | 0…65535, привязка ко всем IPv4 интерфейсам. 0 выбирает свободный порт, но не ограничивает доступ loopback. |
| backlog | Очередь ожидающих соединений; с PHP 8.4 default SOMAXCONN, раньше 128. |
| Результат | Новый слушающий Socket/false; accept вызывается отдельно. |
| Версии/ошибки | PHP 8: объект; PHP 8.5: ValueError для недопустимого порта. Системный отказ — warning + false. |
- Передан порт -1
- PHP 8.5 проверяет диапазон
- ValueError перехвачен
- Сетевой 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 не принимает параметр адреса.
<?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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
- create выделяет endpoint
- close освобождает его
- Возврат null соответствует void
- Повторные операции больше недопустимы
Этот вариант. void означает отсутствие полезного возвращаемого значения, а не bool успеха. Переменная всё ещё может ссылаться на объект Socket, но его системный 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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
| mode | 0 — чтение, 1 — запись, 2 — оба; PHP 8.5 предоставляет SHUT_RD/SHUT_WR/SHUT_RDWR. |
| Результат | true/false. Буферы могут обрабатываться по правилам ОС/протокола; это не прикладное подтверждение доставки. |
| Ошибка | Неверный режим ValueError; системный отказ warning + false. |
- Клиент пишет A42
- shutdown записи сообщает конец запроса
- Сервис читает A42, затем EOF
- Сервис отвечает 7
- Клиент всё ещё может читать
Этот вариант. 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
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 заполняются по ссылке и относятся к выбранной конечной точке.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 для неподдержанного семейства. |
- Есть собственное локальное соединение
- socket_getsockname заполняет $address/$port
- Адрес 127.0.0.1
- Порт проверяется как назначенный, не как заранее известный
Этот вариант. address и port — выходные переменные по ссылке. Функция возвращает bool, а не строку адреса. Числа ephemeral ports намеренно не зафиксированы. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 заполняются по ссылке и относятся к выбранной конечной точке.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 для неподдержанного семейства. |
- Есть собственное локальное соединение
- socket_getpeername заполняет $address/$port
- Адрес 127.0.0.1
- Порт проверяется как назначенный, не как заранее известный
Этот вариант. address и port — выходные переменные по ссылке. Функция возвращает bool, а не строку адреса. Числа ephemeral ports намеренно не зафиксированы. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 заполняет строковую переменную по ссылке.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 | Максимальное число байтов; выбирайте положительный ограниченный размер. |
| mode | PHP_BINARY_READ (default) сохраняет любые байты; PHP_NORMAL_READ останавливается на CR/LF. |
| Результат | Строка данных, пустая строка EOF или false при ошибке. "0" — данные, не отказ. |
| Ошибки | Warning/false с кодом socket_last_error; неверный тип/закрытый Socket дают PHP-ошибку. |
- Отправитель пишет status и LF
- NORMAL_READ читает до LF
- rtrim убирает только CR/LF
- Команда выводится как 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 заполняет строковую переменную по ссылке.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 | Максимум байтов. Не число символов и не гарантированная длина. |
| flags | 0 либо поддерживаемые MSG_* биты; доступность зависит от ОС/протокола. |
| Результат | Число байтов (включая 0) или строго false. Сначала различайте эти значения, затем используйте data. |
- В очередь приходит A42
- PEEK длиной 1 возвращает A
- Очередь не изменилась
- Обычное чтение получает 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 заполняет строковую переменную по ссылке.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 требует ожидания, а не повтора всей строки. |
- write A42 с length=0
- Возврат 0: ничего не отправлено
- Следующая запись передаёт A42
- Приёмник читает 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 заполняет строковую переменную по ссылке.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 фактической длиной; отрицательный недопустим. |
| flags | 0 либо совместимые MSG_* биты, доступные в ОС и выбранном протоколе. |
| Результат | Число принятых байтов либо false. Для TCP допустима частичная запись; успех не подтверждает обработку peer. |
- data содержит 8 байтов
- length=3 выбирает префикс
- send принимает 3 байта
- Приёмник получает 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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 используют явный адрес.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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. |
| flags | MSG_* по договору send и поддержке ОС. |
| address/port | Удалённый IP/порт, для Unix — путь; для IPv4/IPv6 порт нужен. |
| Результат | Число отправленных байтов/false. Это не подтверждение доставки UDP. |
| Версии | PHP 8 nullable port и Socket; PHP 8.5 ValueError для порта вне 0…65535. |
- Receiver привязан к localhost и свободному порту
- Sender отправляет 4 байта на этот порт
- recvfrom получает одну datagram
- На экран выводится T=21
Этот вариант. sendto указывает адрес именно для этой datagram. Наличие локального receiver делает опыт воспроизводимым; обычная успешная UDP-отправка не гарантирует существования получателя или доставки. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 используют явный адрес.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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 для них не подходят.
| Часть договора | Объяснение |
|---|---|
| socket | Datagram endpoint либо иной подходящий соединённый/несоединённый сокет. |
| data | Выходная строка байтов. |
| length/flags | Максимум принимаемых байтов и MSG_* флаги; размер не является гарантией полного сообщения. |
| address/port | Выходной адрес отправителя/порт для unconnected IP; Unix возвращает путь. Не входной фильтр. |
| Результат | Число байтов, включая допустимый 0 для пустого UDP, либо false. |
- Datagram A42=7 приходит на receiver
- recvfrom получает байты и адрес отправителя
- Возврат сообщает 5 байтов
- Приложение видит 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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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-процесс.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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() без объекта. |
- В receiver уже отправлен байт 7
- read хранит stock → receiver
- select оставляет готовый stock
- Чтение получает 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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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-процесс.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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; дальнейшее ожидание организует приложение. |
- Создана пустая соединённая пара
- Receiver переведён в non-blocking
- read возвращает false + would-block
- Программа может перейти к другим действиям
Этот вариант. 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-процесс.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 могут ждать готовности/сигнала/ошибки. Таймаут и срок всей задачи настраивают отдельно. |
- Receiver был non-blocking
- set_block восстанавливает ожидание
- Другой конец пишет ready
- Чтение получает 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 убирает сохранённый код, но ничего не чинит в соединении.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 — ошибки не было либо её явно очистили. |
| Граница | Чтение не очищает код, успешный последующий вызов не следует считать автоматическим сбросом истории. |
- Non-blocking receiver пока пуст
- read вернул false
- last_error(receiver) даёт would-block
- Повторное чтение кода сохраняет значение
Этот вариант. Коды ОС различаются численно, поэтому сравниваем с доступными константами. Последовательные вызовы last_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);
$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 убирает сохранённый код, но ничего не чинит в соединении.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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/пропущен для общего кода. |
| Результат | void; следующий last_error соответствующей области вернёт 0. |
| Граница | Не заменяет исправление причины, повторное подключение или обработку partial data. |
- Read сообщил временный отказ
- Код прочитан
- clear_error(receiver)
- 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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 убирает сохранённый код, но ничего не чинит в соединении.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 не гарантирован. |
- SOCKET_ECONNREFUSED задаёт вид отказа
- strerror запрашивает текст ОС
- Непустая строка пригодна для журнала
- Программа продолжает сравнивать числовой код
Этот вариант. Само описание зависит от ОС и локали, поэтому одинаковую английскую/русскую фразу всем платформам не обещаем. Чтобы увидеть её на своём компьютере, выведите $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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
- 127.0.0.1 + строка 8080
- hints ограничивают IPv4 TCP
- AddressInfo хранит результат
- explain показывает адрес
- connect ещё не вызывался
Этот вариант. Массив hints ограничивает поиск IPv4 и потоком. Числовые флаги запрещают разрешение имени узла/службы. Результат пока только описывает адрес; к порту 8080 никто не подключался.
<?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_family | AF_INET либо AF_INET6. Чтобы не ограничивать семейство, пропустите ключ; явное 0/AF_UNSPEC в PHP 8.5 не проходит проверку. |
| ai_socktype | SOCK_STREAM/SOCK_DGRAM и др.; 0 не ограничивает тип. |
| ai_protocol | SOL_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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
- lookup создаёт AddressInfo
- explain возвращает массив
- ai_family означает IPv4
- ai_socktype означает датаграммы
- 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 всё равно выполняется отдельно.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
- lookup: 127.0.0.1:0 + DGRAM
- addrinfo_bind создаёт Socket
- ОС выбирает свободный порт
- getsockname читает фактический порт
Этот вариант. bind создаёт Socket и привязывает его одним вызовом. Исходный AddressInfo с портом 0 остаётся описанием запроса; фактический порт надо читать у Socket.
<?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. Здесь ни один публичный интерфейс не открывается.
<?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 и подключает его по одному кандидату. Это удобство, но не управляемый таймаут подключения.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 для неверного входа. |
| Ограничение | Вызов может блокироваться по сетевой политике ОС; собственной настройки срока здесь нет. |
- Создан собственный listener
- lookup описывает его порт
- addrinfo_connect создаёт клиента
- accept получает серверную сторону
- A42 доходит до peer
Этот вариант. Весь обмен остаётся в одном процессе и на loopback. Приведение порта к string нужно для service; connect создаёт новый объект, которому затем отправляют три байта. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 недоступен.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 больше не является правильной проверкой. |
- socket_create создаёт результат
- Socket скрывает внутреннюю структуру
- instanceof подтверждает тип
- Дальше вызываются функции 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 здесь пойман ради демонстрации неправильного способа создания.
<?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. Оно ещё не является открытым каналом и не имеет пользовательского конструктора.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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 больше не является правильной проверкой. |
- socket_addrinfo_lookup создаёт результат
- AddressInfo скрывает внутреннюю структуру
- instanceof подтверждает тип
- Дальше вызываются функции 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 здесь пойман ради демонстрации неправильного способа создания.
<?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 не устанавливает значение и не проверяет всю сеть.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
| level | SOL_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; константа чужой платформы может отсутствовать. |
- UDP endpoint уже создан
- level = SOL_SOCKET
- option = SO_TYPE
- Результат равен SOCK_DGRAM
Этот вариант. SOL_SOCKET выбирает общий уровень; SO_TYPE — его конкретное свойство. Это свойство только для чтения; записать новый тип вместо создания другого сокета нельзя.
<?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 отключает алгоритм Нейгла для малых записей; это настройка, не постоянное правило об ускорении.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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_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 и ОС. |
- Socket существует
- SO_RCVTIMEO выбирает чтение
- sec = 2, usec = 0
- set_option возвращает true
- 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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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_IF | PHP принимает индекс интерфейса либо имя; не копируйте структуру 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 не устанавливает значение и не проверяет всю сеть.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
- Создан собственный Socket
- socket_getopt вызывается с SOL_SOCKET
- Операция использует тот же договор
- Поток байтов
Этот вариант. Это 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 отключает алгоритм Нейгла для малых записей; это настройка, не постоянное правило об ускорении.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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_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. |
- Создан собственный Socket
- socket_setopt вызывается с SOL_SOCKET
- Операция использует тот же договор
- 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.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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. |
- Создана локальная пара
- export даёт stream для sender
- fwrite пишет A42
- receiver читает те же три байта
- Владелец закрывает endpoint один раз
Этот вариант. resource stream и объект Socket представляют один endpoint. Здесь закрытие поручено socket_close: повторный fclose того же ресурса не нужен. Для длинных записей нужен цикл обработки частичной записи. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 для сетевой опции, которой может не быть в высокоуровневом интерфейсе. Он не копирует канал.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните 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. |
- Локальный TCP stream уже открыт
- import даёт Socket того же соединения
- set_option задаёт TCP_NODELAY
- Проверяем фактическое значение
Этот вариант. Сначала создан собственный локальный сервер, поэтому внешний адрес не нужен. Импорт полезен для сетевой опции, которой нет в высокоуровневом API; чтение/запись приложения можно оставить stream-функциям. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 сам по себе ещё не означает сеть.
<?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 используют явный адрес.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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; договор вложенных значений приведён ниже. |
| flags | 0 либо поддержанные MSG_* через |: OOB, EOR, DONTWAIT, DONTROUTE. |
| Результат | Число отправленных обычных байтов или false; TCP может принять только часть. |
| Ошибки | Warning/false при преобразовании/системном отказе; TypeError/ValueError для отдельных недопустимых значений. |
- iov: A42= и 7
- name: собственный loopback port
- sendmsg отправляет 5 байтов
- recvfrom получает A42=7
Этот вариант. iov — список буферов; они идут по порядку в одном UDP сообщении. Индексы списка не отправляются. name задаёт получателя, flags передаётся отдельным третьим аргументом. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 | Точный смысл |
|---|---|
| name | IPv4: 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 используют явный адрес.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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 для них не подходят.
| Часть договора | Объяснение |
|---|---|
| socket | Endpoint с ожидаемым сообщением; blocking может ждать. |
| message | Передаваемая по ссылке переменная: сначала размеры/name, после вызова данные/control/flags. |
| flags | 0 либо поддержанные данным recvmsg флаги MSG_*, например PEEK/DONTWAIT. |
| Результат | Количество обычных байтов или строго false; проверяйте также выходные flags. |
| Версии | PHP 5.5; Socket вместо resource с PHP 8. Структура уточнена исходниками 8.5.11. |
- buffer_size=32 и controllen=64
- UDP A42=7 уже отправлена
- recvmsg заполняет iov
- Результат равен 5
- implode соединяет буферы для вывода
Этот вариант. До вызова массив задаёт размеры буферов; после вызова это уже полученное сообщение. controllen здесь небольшой резерв без запроса ancillary-опций; для конкретной структуры используйте cmsg_space. Приложенный local-sockets.php создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 передаёт доступ к уже открытому ресурсу.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Проверен только синтаксис; окружение этого опыта не запускалось. Архив содержит полный код и указанные файлы. Ожидаемый результат относится к описанной ниже среде. Сначала прочитайте требования платформы и README, затем подготовьте отдельный учебный стенд.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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. |
- Выбраны IPPROTO_IPV6 и IPV6_HOPLIMIT
- PHP знает размер структуры
- CMSG_SPACE добавляет заголовок/выравнивание
- Положительный int пригоден для controllen
Этот вариант. Результат включает системные заголовки и выравнивание, а не только размер int. Число зависит от архитектуры; печатаем проверенное свойство, а не выдуманную переносимую константу.
<?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* опций и не получение пакета.
<?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 может означать обычную позицию, поэтому ошибку дополнительно проверяют.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Проверен только синтаксис; окружение этого опыта не запускалось. Архив содержит полный код и указанные файлы. Ожидаемый результат относится к описанной ниже среде. Сначала прочитайте требования платформы и README, затем подготовьте отдельный учебный стенд.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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. |
- Локальный TCP peer открыт
- Старая ошибка очищена
- atmark возвращает false
- Новая ошибка равна 0
- Это обычная позиция, не отказ
Этот вариант. Предусловие: PHP 8.3+ и наличие системного sockatmark, обычно Unix. Эта Windows-сборка проверяет синтаксис примера, а не обещает наличие функции. false при отсутствии метки — нормальный результат, а не всегда failure. Приложенный 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 {
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 описания. Эти три действия имеют разные сроки жизни.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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_id | PID процесса-импортёра. Описание пригодно только ему. |
| Результат | Строковый ID mapping либо false с предупреждением Windows. |
| Доступность | Только Windows, PHP 7.3+; вход/выход Socket с PHP 8. На других системах самой функции нет. |
- Создан собственный UDP endpoint
- export использует getmypid для учебного случая
- export выполняет свою отдельную стадию
- Mapping и Socket освобождаются отдельно
Этот вариант. Windows-only: target PID взят у текущего процесса, чтобы самостоятельный пример не нуждался во внешнем worker. Это демонстрация механизма, не модель распараллеливания. Между процессами ID передают только предназначенному PID.
<?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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 описания. Эти три действия имеют разные сроки жизни.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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_id | ID экспортированного описания, созданного именно для текущего PID. |
| Результат | Новый Socket-handle общего endpoint либо warning/false. |
| Доступность | Только Windows, PHP 7.3+; вход/выход Socket с PHP 8. На других системах самой функции нет. |
- Создан собственный UDP endpoint
- export использует getmypid для учебного случая
- import выполняет свою отдельную стадию
- Mapping и Socket освобождаются отдельно
Этот вариант. Windows-only: target PID взят у текущего процесса, чтобы самостоятельный пример не нуждался во внешнем worker. Это демонстрация механизма, не модель распараллеливания. Между процессами ID передают только предназначенному PID.
<?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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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 описания. Эти три действия имеют разные сроки жизни.
- Приложенный local-net.php создаёт только собственный канал
- Запрос: product:1 с переводом строки либо явно указанная датаграмма
- Ответ содержит настоящие поля name=Молоток, stock=8
- Получатель проверяет формат и показывает название/остаток
Распакуйте все файлы архива вместе и выполните php example.php. Особые расширения и зависимости указаны ниже. Все адреса связи в исполняемых примерах относятся к собственной машине.
Здесь localTcp, writeBytes, readCatalogLine, serveProduct и showProduct — наши помощники. Сначала разберите их полный файл и путь команды. Весь файл приложен; серверные товары и ограничения заданы внутри него.
<?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_id | ID из export этого процесса; освобождается сохранённый shared-memory mapping. |
| Результат | true при удалении известного mapping; false если его нет/уже освобождён. Endpoint не закрывается. |
| Доступность | Только Windows, PHP 7.3+; вход/выход Socket с PHP 8. На других системах самой функции нет. |
- Создан собственный UDP endpoint
- export использует getmypid для учебного случая
- release выполняет свою отдельную стадию
- Mapping и Socket освобождаются отдельно
Этот вариант. Windows-only: target PID взят у текущего процесса, чтобы самостоятельный пример не нуждался во внешнем worker. Это демонстрация механизма, не модель распараллеливания. Между процессами ID передают только предназначенному PID.
<?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 создаёт только собственные локальные соединения и задаёт двухсекундные таймауты ввода/вывода. Его функции разобраны в разделе «Локальная лаборатория». Файл нужно сохранить рядом с примером; никакой внешний сервер не требуется.
<?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. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


