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

Функция socket_getpeername в PHP: практическое применение
Раздел: Сокеты
socket_getpeername(resource socket, string &address [, int &port]): bool
Описание функции socket_getpeername

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

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

Аргументы функции

Функция имеет три параметра:

  1. socket - экземпляр сокета, созданный с помощью socket_create() или принятый через socket_accept().
  2. address - переменная, передаваемая по ссылке, в которую будет записан IP-адрес или путь Unix-сокета.
  3. port - переменная, передаваемая по ссылке, в которую будет записан номер порта (для сетевых сокетов).

Функция возвращает true при успехе и false при ошибке.

Примеры использования socket_getpeername
Пример 1: Получение адреса и порта клиента
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'example.com', 80);

if (socket_getpeername($socket, $address, $port)) {
    echo "Подключено к $address на порту $port";
}
?>
Подключено к 93.184.216.34 на порту 80
Пример 2: Обработка ошибки
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// Сокет не подключен
if (!socket_getpeername($socket, $address, $port)) {
    echo "Ошибка: " . socket_strerror(socket_last_error($socket));
}
?>
Ошибка: Socket is not connected
Пример 3: Использование с Unix-сокетами
<?php
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
socket_connect($socket, '/tmp/my_socket');

if (socket_getpeername($socket, $address)) {
    echo "Подключено к Unix-сокету: $address";
}
?>
Подключено к Unix-сокету: /tmp/my_socket
Похожие функции в PHP

В PHP существуют несколько функций для работы с сокетами, которые могут быть использованы в схожих сценариях:

  • socket_getsockname() - получает локальный адрес и порт сокета. Полезна, когда нужно узнать, на каком интерфейсе и порту работает сервер.
  • stream_socket_get_name() - аналог для потоковых сокетов, созданных через потоковый API. Работает с ресурсами потоков.
  • socket_recvfrom() - получает данные и адрес отправителя для непривилегированных сокетов. Используется для UDP-сокетов.

Выбор функции зависит от контекста: socket_getpeername предназначена для установленных TCP-соединений или Unix-сокетов, тогда как socket_recvfrom подходит для датаграмм.

Альтернативы в других языках программирования
Python: метод getpeername()
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('example.com', 80))
addr, port = s.getpeername()
print(f'Connected to {addr}:{port}')
Connected to 93.184.216.34:80
JavaScript (Node.js): метод remoteAddress и remotePort
const net = require('net');
const socket = net.createConnection(80, 'example.com', () => {
    console.log(`Connected to ${socket.remoteAddress}:${socket.remotePort}`);
});
Connected to 93.184.216.34:80
MySQL: функция CONNECTION_ID()

В MySQL нет прямой аналогии, но можно получить идентификатор соединения для текущей сессии:

SELECT CONNECTION_ID();
42

В отличие от PHP, Python и Node.js предоставляют методы как часть объекта сокета, а не как отдельные функции.

Типичные ошибки при использовании socket_getpeername
Ошибка 1: Передача неподключенного сокета
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_getpeername($socket, $address, $port)) {
    $error = socket_last_error($socket);
    echo "Ошибка: " . socket_strerror($error); // ENOTCONN
}
?>
Ошибка: Socket is not connected
Ошибка 2: Передача неверного типа переменной для адреса или порта
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'example.com', 80);
$address = null;
$port = null;
// Правильно: переменные передаются по ссылке, функция сама их инициализирует
if (socket_getpeername($socket, $address, $port)) {
    echo "Адрес: $address, Порт: $port";
}
?>
Адрес: 93.184.216.34, Порт: 80
Ошибка 3: Использование с неправильным типом сокета
<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// UDP-сокет не устанавливает соединение, поэтому getpeername может не сработать
socket_connect($socket, 'example.com', 80); // connect для UDP только устанавливает адрес по умолчанию
if (socket_getpeername($socket, $address, $port)) {
    echo "Адрес: $address, Порт: $port";
}
?>
Адрес: 93.184.216.34, Порт: 80

Примечание: Для UDP socket_connect() только устанавливает адрес по умолчанию, но соединение не устанавливается в том же смысле, что и для TCP.

Изменения в последних версиях PHP

В PHP 8 функция socket_getpeername() не претерпела значительных изменений. Однако, начиная с PHP 8.0, многие функции сокетов теперь выбрасывают исключение Error при неудаче, если это не отключено. Ранее они возвращали false и устанавливали код ошибки.

Пример в PHP 8:

<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
try {
    socket_getpeername($socket, $address, $port);
} catch (Error $e) {
    echo "Исключение: " . $e->getMessage();
}
?>
Исключение: socket_getpeername(): unable to retrieve peer address [0]: Socket is not connected

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

Расширенные примеры использования socket_getpeername
Пример 1: Логирование подключений в серверном сокете
Пример php
<?php
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 8080);
socket_listen($server);

while (true) {
    $client = socket_accept($server);
    if (socket_getpeername($client, $address, $port)) {
        $logEntry = date('Y-m-d H:i:s') . " - Подключение от $address:$port\n";
        file_put_contents('connections.log', $logEntry, FILE_APPEND);
    }
    socket_close($client);
}
?>
Пример 2: Проверка подключения к доверенным адресам
Пример php
<?php
$trusted = ['192.168.1.1', '10.0.0.2'];
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '192.168.1.1', 80);

if (socket_getpeername($socket, $address, $port)) {
    if (in_array($address, $trusted)) {
        echo "Доверенное подключение к $address\n";
    } else {
        echo "Подключение к недоверенному адресу: $address\n";
    }
}
?>
Доверенное подключение к 192.168.1.1
Пример 3: Использование с IPv6
Пример php
<?php
$socket = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'ipv6.google.com', 80);

if (socket_getpeername($socket, $address, $port)) {
    echo "Подключение к IPv6: $address:$port\n";
}
?>
Подключение к IPv6: 2a00:1450:401b:801::200e:80
Пример 4: Многопоточный сервер с обработкой клиентов
Пример php
<?php
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 9000);
socket_listen($server);

$clients = [];

while (true) {
    $client = socket_accept($server);
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('Ошибка fork');
    } else if ($pid) {
        // Родительский процесс
        $clients[] = $pid;
        socket_close($client);
    } else {
        // Дочерний процесс
        if (socket_getpeername($client, $address, $port)) {
            echo "[PID: ".getpid()."] Обработка клиента $address:$port\n";
            // Обработка данных...
        }
        socket_close($client);
        exit(0);
    }
}
?>
Пример 5: Получение информации о подключении к Unix-сокету
Пример php
<?php
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
$serverSocket = '/tmp/server.sock';

// Убедимся, что серверный сокет существует
if (file_exists($serverSocket)) {
    socket_connect($socket, $serverSocket);
    if (socket_getpeername($socket, $address)) {
        echo "Подключено к Unix-сокету: $address\n";
    }
} else {
    echo "Серверный сокет не найден\n";
}
?>
Подключено к Unix-сокету: /tmp/server.sock

Эти примеры демонстрируют разнообразные сценарии применения функции в реальных проектах.

PHP socket_getpeername function comments

En
Socket getpeername Queries the remote side of the given socket which may either result in host/port or in a Unix filesystem path, dependent on its type