Socket recvfrom: примеры (PHP)

socket_recvfrom в PHP: практическое применение
Раздел: Сокеты
socket_recvfrom(resource socket, string &buf, int len, int flags, string &name [, int &port]): int

Основы функции socket_recvfrom

Назначение

Функция socket_recvfrom применяется для получения данных из сокета, преимущественно при работе с UDP-протоколом. Она позволяет получить не только данные, но и адрес отправителя.

Синтаксис
socket_recvfrom(Socket $socket, string &$data, int $length, int $flags, string &$address, int &$port = null): int|false
Аргументы
$socket

Созданный ресурс сокета, возвращенный функцией socket_create.

&$data

Переменная, куда будут записаны полученные данные.

$length

Максимальное количество байт для чтения.

$flags

Битовое значение флагов: MSG_OOB, MSG_PEEK, MSG_WAITALL, MSG_DONTWAIT.

&$address

Переменная для IP-адреса отправителя.

&$port

Переменная для порта отправителя.

Базовые примеры использования

Пример с UDP-сервером
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($socket, '127.0.0.1', 9999);
$data = '';
$from = '';
$port = 0;
$received = socket_recvfrom($socket, $data, 1024, 0, $from, $port);
echo "Получено $received байт от $from:$port\n";
echo "Данные: $data\n";
Получено 12 байт от 127.0.0.1:54321
Данные: Hello World!
Использование флага MSG_PEEK
socket_recvfrom($socket, $data, 1024, MSG_PEEK, $from, $port);
echo "Данные остаются в буфере: $data";
Данные остаются в буфере: Test message

Похожие функции в PHP

Читает данные из подключенного сокета, но не возвращает адрес отправителя. Используется для TCP-соединений.

stream_socket_recvfrom

Работает с потоковыми сокетами, возвращает данные без изменения буфера. Удобна для работы с файловыми дескрипторами.

Читает данные из сокета с заданной длиной, но без поддержки флагов и адреса отправителя.

Аналоги в других языках

Python: socket.recvfrom
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('127.0.0.1', 9999))
data, addr = sock.recvfrom(1024)
print(f"Получено от {addr}: {data.decode()}")
Node.js: dgram.createSocket
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
  console.log(`Получено от ${rinfo.address}:${rinfo.port} - ${msg}`);
});
server.bind(9999);
C: recvfrom()

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

Распространенные ошибки

Неинициализированные переменные
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$result = socket_recvfrom($socket, $data, 1024, 0, $from, $port);
// Переменные $from и $port передаются по ссылке, но не инициализированы
Результат
Предупреждение: socket_recvfrom(): 4-й аргумент должен быть корректной переменной
Неправильный тип сокета
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// Для TCP сокетов socket_recvfrom не подходит
socket_recvfrom($socket, $data, 1024, 0, $from, $port);
Возвращает false, устанавливается код ошибки SOCKET_EOPNOTSUPP

Изменения в версиях PHP

PHP 8.0

Изменена сигнатура функции: параметры $address и $port теперь передаются по ссылке (добавлен амперсанд &). Ранее они передавались по значению.

PHP 7.2

Добавлена поддержка флага MSG_DONTWAIT для неблокирующих операций.

Расширенные примеры

Многопоточный UDP-сервер
Пример php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_nonblock($socket);
socket_bind($socket, '0.0.0.0', 8888);
$clients = [];
while (true) {
    $data = '';
    $from = '';
    $port = 0;
    $len = @socket_recvfrom($socket, $data, 65535, 0, $from, $port);
    if ($len !== false) {
        $key = $from . ':' . $port;
        $clients[$key] = time();
        echo "[$key] $data\n";
        // Ответ клиенту
        socket_sendto($socket, "OK\n", 3, 0, $from, $port);
    }
    usleep(100000);
}
Обработка бинарных данных
Пример php
$received = socket_recvfrom($socket, $binary, 4, 0, $from, $port);
$unpacked = unpack('Nint', $binary);
echo "Получено 32-битное число: " . $unpacked['int'];
Использование MSG_WAITALL
Пример php
// Ждет получения точного количества байт
$len = socket_recvfrom($socket, $data, 512, MSG_WAITALL, $from, $port);
if ($len === 512) {
    echo "Получен полный пакет";
}

PHP socket_recvfrom function comments

En
Socket recvfrom Receives data from a socket, whether it is connected or not