Socket sendto: примеры (PHP)
socket_sendto(resource socket, string buf, int len, int flags, string addr [, int port]): intОсновные сведения о socket_sendto
socket_sendto — это низкоуровневая функция в PHP, предназначенная для отправки данных через сетевой сокет без установки соединения, преимущественно по протоколу UDP или для работы с RAW-сокетами. В отличие от функций для TCP, она не требует предварительного подключения к удаленному узлу.
Функция принимает шесть параметров:
- $socket (Socket) — ресурс сокета, созданный функцией socket_create.
- $data (string) — данные для отправки в виде строки.
- $length (int) — количество байт для отправки из $data.
- $flags (int) — битовая маска флагов отправки. MSG_EOR, MSG_OOB, MSG_DONTROUTE, MSG_CONFIRM и другие, определенные системой.
- $address (string) — IP-адрес получателя.
- $port (int|null) — порт получателя. Для RAW-сокетов может быть 0 или null.
Функция возвращает количество отправленных байт или false в случае ошибки.
Короткие примеры использования
<?php
// Создание UDP сокета
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) {
echo "Ошибка создания сокета: " . socket_strerror(socket_last_error());
exit;
}
$data = "Hello UDP!\n";
$len = strlen($data);
$flags = 0;
$address = '127.0.0.1';
$port = 12345;
$sent = socket_sendto($socket, $data, $len, $flags, $address, $port);
if ($sent !== false) {
echo "Отправлено байт: $sent";
} else {
echo "Ошибка отправки: " . socket_strerror(socket_last_error($socket));
}
socket_close($socket);
?>Отправлено байт: 11
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$data = "Test data";
$sent = socket_sendto($socket, $data, strlen($data), MSG_DONTROUTE, '10.0.0.1', 9999);
if ($sent === false) {
echo "Ошибка (возможно, маршрут недоступен): " . socket_strerror(socket_last_error($socket));
}
socket_close($socket);
?>Ошибка (возможно, маршрут недоступен): Network is unreachable
Похожие функции в PHP
Используется для записи в потоковый сокет (TCP). Требует предварительного соединения через socket_connect или socket_accept. Не требует указания адреса и порта для каждого вызова.
Аналогична socket_sendto, но работает с уже подключенными сокетами. Применяется для TCP или подключенных UDP-сокетов.
Функция для потоков. Работает с сокетами, созданными через stream_socket_server/client. Поддерживает контексты потоков и таймауты.
socket_sendto предпочтительнее для сценариев, где адрес получателя может меняться для каждого пакета (например, UDP-сервер для ответов разным клиентам). Для постоянных TCP-соединений лучше подходят socket_write или socket_send.
Альтернативы в других языках
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sent = sock.sendto(b"Hello", ('127.0.0.1', 12345))
print(f"Отправлено: {sent}")Отправлено: 5
Отличие: Python работает с объектами сокетов, а не ресурсами. Метод является частью объекта. В PHP функция является процедурной.
const dgram = require('dgram');
const socket = dgram.createSocket('udp4');
socket.send('Hello', 12345, '127.0.0.1', (err) => {
if (err) console.error(err);
socket.close();
});Отличие: Асинхронный API с callback-функцией. В PHP socket_sendto работает синхронно.
package main
import "net"
func main() {
conn, _ := net.DialUDP("udp", nil, &net.UDPAddr{IP: net.IPv4(127,0,0,1), Port: 12345})
defer conn.Close()
conn.Write([]byte("Hello"))
}Отличие: Go использует интерфейс io.Writer. Требует явного создания объекта соединения.
Типичные ошибки
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$data = "1234567890";
// Указана длина больше, чем реальная строка
$result = socket_sendto($socket, $data, 20, 0, '127.0.0.1', 9999);
if ($result === false) {
echo "Ошибка: " . socket_strerror(socket_last_error($socket));
}
?>Ошибка: Invalid argument
<?php
// Создание TCP сокета, но соединение не устанавливается
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_sendto($socket, "data", 4, 0, '127.0.0.1', 80);
if ($result === false) {
echo "Ошибка: " . socket_strerror(socket_last_error($socket));
}
?>Ошибка: Socket is not connected
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$result = socket_sendto($socket, "data", 4, 0, '999.999.999.999', 80);
if ($result === false) {
echo "Ошибка: " . socket_strerror(socket_last_error($socket));
}
?>Ошибка: Invalid argument
Изменения в последних версиях PHP
В PHP 8.0 тип параметра $socket изменен с ресурса (resource) на объект класса Socket. Код, который использовал is_resource($socket), требует обновления.
<?php
// PHP 7.x
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
var_dump(is_resource($socket)); // bool(true)
// PHP 8.0+
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
var_dump($socket instanceof \Socket); // bool(true)
?>bool(true) bool(true)
Аргумент $port стал необязательным (допускается null) для совместимости с RAW-сокетами, где номер порта не используется.
Расширенные примеры
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// Простой DNS запрос для example.com (упрощенный, без полного разбора)
$dnsRequest = "\xaa\xaa\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\x01\x00\x01";
$sent = socket_sendto($socket, $dnsRequest, strlen($dnsRequest), 0, '8.8.8.8', 53);
if ($sent !== false) {
echo "DNS запрос отправлен. Ждем ответ...\n";
// Чтение ответа
socket_recvfrom($socket, $buffer, 512, 0, $from, $port);
echo "Ответ от $from:$port, длина: " . strlen($buffer) . " байт";
} else {
echo "Ошибка отправки DNS запроса";
}
socket_close($socket);
?>DNS запрос отправлен. Ждем ответ... Ответ от 8.8.8.8:53, длина: 72 байт
<?php
// Только для Linux, требует запуск с правами root
$socket = socket_create(AF_INET, SOCK_RAW, getprotobyname('icmp'));
if (!$socket) die("Не удалось создать RAW сокет\n");
// Простейший ICMP Echo запрос (тип 8, код 0)
$checksum = 0;
$icmpPacket = pack('CCnnn', 8, 0, $checksum, 1, 1);
// Рассчет контрольной суммы
$sum = 0;
for ($i = 0; $i < strlen($icmpPacket); $i += 2) {
$sum += ord($icmpPacket[$i]) << 8 | ord($icmpPacket[$i+1]);
}
$sum = ($sum >> 16) + ($sum & 0xffff);
$sum += $sum >> 16;
$checksum = ~$sum & 0xffff;
$icmpPacket = pack('CCnnn', 8, 0, $checksum, 1, 1);
$sent = socket_sendto($socket, $icmpPacket, strlen($icmpPacket), 0, '8.8.8.8', 0); // Порт 0 для ICMP
if ($sent) {
echo "ICMP пакет отправлен на 8.8.8.8\n";
}
socket_close($socket);
?>ICMP пакет отправлен на 8.8.8.8
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// Включение опции повторного использования адреса
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
// Привязка сокета к порту
socket_bind($socket, '0.0.0.0', 54321);
// Включение членства в multicast группе
$multicastIp = '239.255.255.250';
$interface = '0.0.0.0';
$mreq = pack('I', ip2long($multicastIp)) . pack('I', ip2long($interface));
socket_set_option($socket, IPPROTO_IP, MCAST_JOIN_GROUP, $mreq);
// Отправка сообщения в multicast группу
$data = "Multicast test message";
$sent = socket_sendto($socket, $data, strlen($data), 0, $multicastIp, 54321);
if ($sent) {
echo "Multicast сообщение отправлено в группу $multicastIp\n";
}
socket_close($socket);
?>Multicast сообщение отправлено в группу 239.255.255.250