Сетевые подключения через сокеты в PHP: методы и примеры

Раздел: Системное администрирование -> Сетевое программирование

Основные подходы к организации сокетных соединений в PHP

Наиболее эффективный способ: использование расширения sockets

Для низкоуровневой работы с сокетами в PHP применяется расширение sockets. Оно позволяет создавать, подключать и передавать данные через TCP или UDP. Основные шаги: создание сокета функцией socket_create(), установка соединения через socket_connect(), отправка и приём данных.


// Пример TCP-соединения с удалённым сервером
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    echo "Ошибка создания сокета: " . socket_strerror(socket_last_error()) . "\n";
    exit;
}

$address = '93.184.216.34'; // example.com
$port = 80;
$result = socket_connect($socket, $address, $port);
if ($result === false) {
    echo "Ошибка соединения: " . socket_strerror(socket_last_error($socket)) . "\n";
    exit;
}

// Отправка HTTP-запроса
$request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
socket_write($socket, $request, strlen($request));

// Чтение ответа
$response = '';
while ($buffer = socket_read($socket, 1024)) {
    $response .= $buffer;
}
echo $response;

socket_close($socket);
  

Php socket connect (подключение через сокеты в php)

Пояснение: AF_INET означает семейство адресов IPv4, SOCK_STREAM - потоковый сокет (TCP). После соединения данные передаются через socket_write() и socket_read(). Ошибки обрабатываются с помощью socket_last_error() и socket_strerror().

Возможные проблемы:

  • Расширение sockets может быть не включено в конфигурации PHP. Проверьте наличие в php.ini директивы extension=sockets.
  • Блокирующий режим работы сокета по умолчанию: если сервер не отвечает, скрипт зависает. Для установки неблокирующего режима используйте socket_set_nonblock($socket).
  • Таймаут соединения не задан явно. Функция socket_connect() может ждать долго. Рекомендуется предварительно установить таймаут через socket_set_option().
  • Игнорирование кодов ошибок: всегда проверяйте возвращаемые значения.

Как установить соединение с помощью fsockopen для HTTP?

fsockopen() - более высокоуровневая функция, которая открывает сокетное соединение и возвращает файловый указатель. Она удобна для протоколов, работающих поверх TCP (HTTP, FTP).


$fp = @fsockopen('example.com', 80, $errno, $errstr, 30);
if (!$fp) {
    echo "Ошибка: $errno - $errstr\n";
    exit;
}

fwrite($fp, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
while (!feof($fp)) {
    echo fgets($fp, 1024);
}
fclose($fp);
  

Php port 80 (порт 80 в php)

Пятый параметр - таймаут в секундах. Функция возвращает ресурс или false при ошибке.

Типичная ошибка:

  • Некорректное использование подавления ошибок (@). Лучше проверять возвращаемое значение и обрабатывать $errno.
  • Неверный формат запроса: HTTP требуется строго соблюдать .

Как использовать stream_socket_client для подключения с таймаутом?

stream_socket_client() - универсальная функция, работающая с потоками. Позволяет задавать таймауты, протокол (ssl://, tcp://) и флаги.


$context = stream_context_create([
    'ssl' => [
        'verify_peer' => true,
        'verify_peer_name' => true,
    ]
]);

$client = @stream_socket_client(
    'tcp://93.184.216.34:80',
    $errno,
    $errstr,
    5,
    STREAM_CLIENT_CONNECT,
    $context
);

if ($client === false) {
    echo "Не удалось подключиться: $errstr ($errno)\n";
    exit;
}

fwrite($client, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
echo stream_get_contents($client);
fclose($client);
  

Преимущество: поддержка SSL/TLS через префикс ssl:// и контекст. Также можно установить неблокирующий режим с помощью stream_set_blocking($client, false).

Частые проблемы:

  • Неверный URI: забывают указать порт или протокол.
  • SSL-ошибки: требуется корректная настройка CA-сертификатов в контексте.
  • Игнорирование таймаута при использовании потоковых функций fread/fgets.

Какие альтернативы существуют для HTTP-запросов через cURL?

Расширение cURL предоставляет удобный интерфейс для работы с HTTP и другими протоколами. Внутри оно также использует сокеты, но даёт больше возможностей: поддержка кук, заголовков, прокси.


$ch = curl_init('http://example.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
if (curl_errno($ch)) {
    echo 'Ошибка cURL: ' . curl_error($ch);
}
curl_close($ch);
echo $response;
  

Для кастомных сокетов cURL не предназначен, но для веб-запросов это самый надёжный вариант.

Проблемы:

  • Требуется установленное расширение curl.
  • Не подходит для не-HTTP протоколов, таких как собственный TCP-обмен.

Расширенные примеры сокетного программирования в PHP

Неблокирующий сокет с таймаутом

Неблокирующий режим позволяет выполнять другие операции во время ожидания ответа. Пример с сокетом и функцией socket_select():

Пример

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_nonblock($socket);

$address = '93.184.216.34';
$port = 80;
$result = @socket_connect($socket, $address, $port);

// socket_connect в неблокирующем режиме сразу вернёт false с ошибкой EINPROGRESS
$write = [$socket];
$except = null;
$timeout_sec = 5;
$timeout_usec = 0;

if (socket_select($null = null, $write, $except, $timeout_sec, $timeout_usec) > 0) {
    // Соединение установлено
    $request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
    socket_write($socket, $request);

    $read = [$socket];
    $response = '';
    $time = time() + 5;
    while (time() < $time) {
        $ready = socket_select($read, $null, $null, 2);
        if ($ready > 0 && in_array($socket, $read)) {
            $chunk = socket_read($socket, 4096);
            if ($chunk === false || $chunk === '') break;
            $response .= $chunk;
        }
    }
    echo $response;
} else {
    echo "Таймаут соединения\n";
}
socket_close($socket);
HTTP/1.1 200 OK
Content-Type: text/html
...(полный ответ сервера)

Пояснение: после вызова socket_connect() в неблокирующем режиме ждём завершения соединения через socket_select. Затем отправляем запрос и читаем ответ, контролируя таймаут.

Подключение через SSL/TLS с проверкой сертификата

Для защищённых соединений используется потоковый контекст с SSL-опциями. Пример с stream_socket_client:

Пример

$options = [
    'ssl' => [
        'verify_peer'      => true,
        'verify_peer_name' => true,
        'allow_self_signed' => false,
        'cafile'           => '/path/to/cacert.pem'
    ]
];
$context = stream_context_create($options);

$client = stream_socket_client(
    'ssl://example.com:443',
    $errno,
    $errstr,
    10,
    STREAM_CLIENT_CONNECT,
    $context
);

if ($client) {
    fwrite($client, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
    $body = stream_get_contents($client);
    echo $body;
    fclose($client);
} else {
    echo "SSL ошибка: $errstr ($errno)\n";
}
... (содержимое сайта по HTTPS)

Использование сокетов для отправки произвольных данных (например, Syslog)

Протокол Syslog работает через UDP на порту 514. Пример отправки сообщения:

Пример

$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$message = "<14>Jan  1 12:34:56 myhost test message";
socket_sendto($socket, $message, strlen($message), 0, 'syslog.example.com', 514);
socket_close($socket);
echo "Сообщение отправлено\n";
Сообщение отправлено

Пояснение: UDP не требует установки соединения - данные отправляются сразу. Функция socket_sendto() используется для отправки датаграммы.

Многопоточная обработка множества сокетов с socket_select

Можно обслуживать несколько подключений одновременно. Пример сервера, который принимает соединения и читает данные:

Пример

$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 8080);
socket_listen($server);
socket_set_nonblock($server);

$clients = [];
$start = time();

while (time() - $start < 10) { // работаем 10 секунд
    $read = array_merge([$server], $clients);
    $write = null;
    $except = null;
    if (socket_select($read, $write, $except, 1) > 0) {
        if (in_array($server, $read)) {
            $new = socket_accept($server);
            if ($new) {
                socket_set_nonblock($new);
                $clients[] = $new;
                echo "Новое соединение\n";
            }
        }
        foreach ($clients as $i => $client) {
            if (in_array($client, $read)) {
                $data = socket_read($client, 1024);
                if ($data === false || $data === '') {
                    socket_close($client);
                    unset($clients[$i]);
                    echo "Клиент отключился\n";
                } else {
                    echo "Получено: $data\n";
                }
            }
        }
    }
}

foreach ($clients as $client) socket_close($client);
socket_close($server);

Результат: скрипт обрабатывает подключения, не блокируя выполнение. socket_select позволяет отслеживать множество дескрипторов.

Подключение через сокеты в PHP - comments

En
Php socket connect (php)