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

Работа с сокетами: использование функции socket_recv
Раздел: Сокеты
socket_recv(resource socket, string &buf, int len, int flags): int
Описание функции socket_recv

Функция socket_recv() используется в PHP для получения данных из подключенного сокета. Она работает на более низком уровне по сравнению с функциями вроде fread() и предоставляет контроль над операцией чтения через флаги.

Когда используется

Функция применяется в сетевом программировании для создания TCP/UDP клиентов и серверов, где требуется точное управление процессом чтения данных из сокета, например, в протоколах с бинарными данными или при необходимости просмотра данных без их извлечения из буфера.

Аргументы функции
  • socket (Socket): Объект сокета, созданный функцией socket_create().
  • buf (string): Переменная, в которую будут помещены принятые данные. Передается по ссылке.
  • len (int): Максимальное количество байт для чтения.
  • flags (int): Комбинация флагов, управляющих чтением:
    • MSG_OOB - чтение данных вне потока (out-of-band).
    • MSG_PEEK - получение данных из начала буфера, но без их удаления из буфера.
    • MSG_WAITALL - блокировка до тех пор, пока не будет прочитано запрошенное количество байт (len). Может не выполняться при получении сигнала или обрыве соединения.
    • MSG_DONTWAIT - чтение в неблокирующем режиме.

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

Краткие примеры использования
Пример 1: Базовое чтение
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8080);
$buf = '';
$bytes = socket_recv($socket, $buf, 1024, 0);
echo "Получено $bytes байт: $buf";
?>
Получено 12 байт: Hello World!
Пример 2: Использование флага MSG_PEEK
<?php
$socket = //... подключенный сокет
$buf = '';
// Прочитать данные, но оставить их в буфере сокета
$bytes = socket_recv($socket, $buf, 5, MSG_PEEK);
echo "Заглянули в буфер: $buf\n";
// Чтение еще раз — данные все еще там
$buf2 = '';
$bytes2 = socket_recv($socket, $buf2, 5, 0);
echo "Прочитали окончательно: $buf2";
?>
Заглянули в буфер: Hello
Прочитали окончательно: Hello
Пример 3: Использование MSG_WAITALL
<?php
$socket = //... подключенный сокет
$buf = '';
// Будет ждать ровно 8 байт
$bytes = socket_recv($socket, $buf, 8, MSG_WAITALL);
echo "Получено $bytes байт: $buf";
?>
Получено 8 байт: Data1234
Похожие функции в PHP

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

$data = socket_read($socket, 1024);
stream_socket_recvfrom()

Работает с потоковыми сокетами, созданными через stream_socket_client()/stream_socket_server(). Поддерживает флаги, схожие с socket_recv(). Рекомендуется при использовании потокового контекста вместо расширения Socket.

$data = stream_socket_recvfrom($socket, 1024, 0, $peer);
Аналоги в других языках
Python: socket.recv()

Метод recv() объекта сокета в Python принимает размер буфера и флаги. Похож на PHP-версию, но флаги (например, MSG_PEEK) импортируются из модуля socket.

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8080))
data = s.recv(1024)
print(f"Received: {data}")
Received: b'Hello'
JavaScript (Node.js): socket.read()

В Node.js сокеты являются потоками. Метод read() считывает данные, если они доступны во внутреннем буфере. Для низкоуровневого контроля используется модуль net или dgram с событиями 'data'.

const net = require('net');
const client = net.connect(8080, '127.0.0.1', () => {});
client.on('data', (data) => {
  console.log(`Received: ${data}`);
});
Received: Hello
Типичные ошибки
Передача неподходящего типа сокета

Функция ожидает ресурс или объект Socket. Передача другого типа приводит к ошибке.

<?php
$socket = 'not_a_socket';
$buf = '';
$result = socket_recv($socket, $buf, 1024, 0); // Warning: socket_recv() expects parameter 1 to be Socket, string given
?>
Warning: socket_recv() expects parameter 1 to be Socket, string given
Неинициализированный буфер

Передача неинициализированной переменной в качестве буфера может вызвать неожиданное поведение, хотя в современных версиях PHP это часто работает.

<?php
$socket = //... сокет
// $buf не объявлена
$bytes = socket_recv($socket, $buf, 1024, 0); // Переменная $buf будет создана
?>
Некорректная длина чтения

Указание нулевой или отрицательной длины приводит к тому, что функция не читает данные.

<?php
$socket = //... сокет
$buf = '';
$bytes = socket_recv($socket, $buf, 0, 0);
var_dump($bytes); // int(0)
?>
int(0)
Изменения в последних версиях PHP

В PHP 8.0.0 параметр socket ожидает экземпляр класса Socket, а не ресурс (resource). В более ранних версиях передавался ресурс. Это изменение является частью общей типизации объектов вместо ресурсов.

// До PHP 8.0.0
$socket = socket_create(...); // Тип: resource
// Начиная с PHP 8.0.0
$socket = socket_create(...); // Тип: Socket
Расширенные примеры
Пример: Чтение бинарных данных с заголовком

Часто в протоколах сначала передается размер пакета, затем данные. Использование MSG_WAITALL гарантирует чтение точного количества байт.

Пример php
<?php
function read_packet($socket) {
    $header = '';
    // Читаем 4 байта (размер пакета в бинарном формате)
    socket_recv($socket, $header, 4, MSG_WAITALL);
    $size = unpack('N', $header)[1]; // Распаковываем беззнаковое 32-битное число (big endian)
    $data = '';
    if ($size > 0) {
        socket_recv($socket, $data, $size, MSG_WAITALL);
    }
    return $data;
}
?>
Пример: Работа с неблокирующим сокетом

Использование флага MSG_DONTWAIT позволяет избежать блокировки, если данных нет.

Пример php
<?php
socket_set_nonblock($socket);
$buf = '';
$bytes = socket_recv($socket, $buf, 1024, MSG_DONTWAIT);
if ($bytes === false) {
    $error = socket_last_error($socket);
    if ($error == SOCKET_EAGAIN || $error == SOCKET_EWOULDBLOCK) {
        echo "Данных пока нет.\n";
    } else {
        echo "Произошла реальная ошибка.";
    }
} else {
    echo "Получено $bytes байт.";
}
?>
Пример: Обработка данных вне потока (OOB)

Флаг MSG_OOB позволяет читать срочные данные, отправленные вне основного потока.

Пример php
<?php
$oob_data = '';
// Попытка чтения внеполосных данных
$bytes = socket_recv($socket, $oob_data, 1024, MSG_OOB);
if ($bytes > 0) {
    echo "Срочные данные: $oob_data";
}
?>

PHP socket_recv function comments

En
Socket recv Receives data from a connected socket