Socket last error: примеры (PHP)

socket_last_error в PHP: практическое применение
Раздел: Сокеты
socket_last_error(resource socket): int

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

Описание

Функция socket_last_error возвращает последнюю ошибку, которая произошла при работе с сокетами. Она используется для отладки сетевых операций после вызовов функций вроде socket_connect, socket_bind или socket_read.

Аргументы

Функция принимает один необязательный аргумент:

  • $socket (Socket|null) - ресурс сокета, созданный функцией socket_create. Если передан null или аргумент опущен, функция возвращает последнюю ошибку глобального контекста сокетов (до PHP 8.0.0) или вызывает ошибку (начиная с PHP 8.0.0).

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

Базовый пример с сокетом

Получение кода ошибки после неудачного подключения:

<?
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// Пытаемся подключиться к несуществующему порту
if (!socket_connect($socket, '127.0.0.1', 9999)) {
    $error_code = socket_last_error($socket);
    $error_msg = socket_strerror($error_code);
    echo "Код ошибки: $error_code, сообщение: $error_msg";
}
socket_close($socket);
?>
Код ошибки: 111, сообщение: Connection refused
Пример с глобальной ошибкой (устаревшее)

Версия для PHP до 8.0.0:

<?
// В старых версиях можно было вызвать без аргумента
$socket = socket_create(AF_INET, SOCK_STREAM, 999); // Неверный протокол
if ($socket === false) {
    $error_code = socket_last_error(); // Без параметра
    echo "Код: $error_code";
}
?>

Альтернативные функции в PHP

Возвращает массив с информацией о сокете, включая ошибки в потоковом контексте. Используется чаще для потоков, чем для чистых сокетов.

Сбрасывает последнюю ошибку сокета или глобальную ошибку. Полезно использовать перед операцией, чтобы избежать обработки старых ошибок.

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

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

Socket last error в Python

Используется исключение socket.error или метод socket.getsockopt:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    s.connect(('127.0.0.1', 9999))
except socket.error as e:
    print(f'Ошибка: {e.errno} - {e.strerror}')
JavaScript (Node.js)

Ошибки обрабатываются через события 'error' или колбэки:

const net = require('net');
const client = net.connect({port: 9999}, () => {});
client.on('error', (err) => {
    console.log(`Код: ${err.code}, сообщение: ${err.message}`);
});

Socket last error в C

Используется глобальная переменная errno и функция perror:

int sock = socket(AF_INET, SOCK_STREAM, 0);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
    printf("Ошибка %d: %s", errno, strerror(errno));
}

Типичные ошибки

Использование без аргумента в PHP 8+

Вызов функции без параметра приводит к фатальной ошибке:

<?
// PHP 8.0 и выше
$error = socket_last_error(); // TypeError
?>
TypeError: socket_last_error() expects exactly 1 argument, 0 given
Передача нересурсного значения

Передача некорректного сокета:

<?
$fake_socket = null;
$error = socket_last_error($fake_socket); // В PHP 8 - TypeError
?>
Неочистка старой ошибки

Ошибка может относиться к предыдущей операции:

<?
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 9999); // Ошибка
$error1 = socket_last_error($socket); // Код 111

// Новая операция без ошибки
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
// Но старая ошибка осталась, если не очистить
$error2 = socket_last_error($socket); // Все еще 111
?>

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

PHP 8.0.0

Параметр $socket стал обязательным. Вызов функции без аргумента теперь вызывает TypeError. Глобальное хранилище ошибок сокетов удалено.

PHP 7.0.0

Тип параметра $socket изменен с ресурса (resource) на объект Socket.

PHP 5.3.0

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

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

Мониторинг нескольких сокетов

Обработка ошибок в цикле select:

<?
$sockets = [];
for ($i = 0; $i < 3; $i++) {
    $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    socket_set_nonblock($sock);
    socket_connect($sock, '192.168.1.' . $i, 80);
    $sockets[] = $sock;
}

usleep(100000); // Ждем асинхронного подключения

foreach ($sockets as $sock) {
    $error = socket_last_error($sock);
    if ($error !== 0 && $error !== 115) { // 115 = EINPROGRESS
        echo 'Сокет ' . spl_object_id($sock) . 
             ' ошибка: ' . socket_strerror($error) . '
'; socket_clear_error($sock); } } ?>
Кастомная обработка ошибок UDP

Работа с датаграммами:

<?
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($socket, '127.0.0.1', 0);

// Пытаемся отправить на закрытый порт
$sent = socket_sendto($socket, 'test', 4, 0, '127.0.0.1', 9999);
if ($sent === false) {
    $error = socket_last_error($socket);
    // Для UDP ошибка может не возвращаться сразу
    if ($error === 0) {
        usleep(10000);
        $error = socket_last_error($socket);
    }
    echo 'UDP ошибка: ' . socket_strerror($error);
}
?>
Интеграция с системой логирования

Расширенный обработчик:

<?
class SocketErrorHandler {
    private $errors = [];
    
    public function check($socket, string $operation): bool {
        $error = socket_last_error($socket);
        if ($error !== 0) {
            $this->errors[] = [
                'time' => microtime(true),
                'code' => $error,
                'msg' => socket_strerror($error),
                'op' => $operation
            ];
            socket_clear_error($socket);
            return false;
        }
        return true;
    }
    
    public function getErrors(): array {
        return $this->errors;
    }
}

$handler = new SocketErrorHandler();
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if (!socket_connect($sock, 'example.com', 9999)) {
    $handler->check($sock, 'connect');
}

print_r($handler->getErrors());
?>
Работа с мультиплексированием

Использование с socket_select:

<?
$read = [$socket];
$write = [$socket];
$except = [$socket];

$changed = socket_select($read, $write, $except, 5);

if ($changed === false) {
    $error = socket_last_error($socket);
    echo 'Select ошибка: ' . socket_strerror($error);
} elseif ($changed > 0) {
    if (in_array($socket, $except, true)) {
        $error = socket_last_error($socket);
        echo 'Исключительная ситуация: ' . socket_strerror($error);
    }
}
?>

PHP socket_last_error function comments

En
Socket last error Returns the last error on the socket