Fsockopen: примеры (PHP)

Руководство по использованию fsockopen для работы с сокетами
Раздел: Сетевые функции
fsockopen(string $hostname, int $port = -1, int &$error_code = null, string &$error_message = null, ?float $timeout = null): resource|false

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

Функция fsockopen в PHP используется для установки сетевого соединения через сокеты. Она позволяет работать с различными протоколами, включая TCP и UDP. Основное применение — создание низкоуровневых соединений для реализации клиентов протоколов HTTP, SMTP, FTP или собственных протоколов.

Аргументы функции
  • hostname — имя хоста или IP-адрес сервера. Может быть доменным именем, IPv4 или IPv6 адресом.
  • port — номер порта для подключения. Для HTTP обычно 80, для HTTPS — 443.
  • error_code — переменная, в которую будет записан код ошибки при неудачном подключении.
  • error_string — переменная для получения текстового описания ошибки.
  • timeout — время ожидания соединения в секундах. Значение по умолчанию зависит от настроек PHP.
  • flags — дополнительные флаги для управления типом соединения.
Флаги соединения
  • STREAM_CLIENT_CONNECT — создание постоянного соединения.
  • STREAM_CLIENT_ASYNC_CONNECT — асинхронное подключение.
  • STREAM_CLIENT_PERSISTENT — создание постоянного соединения.

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

Базовое HTTP-подключение
$fp = fsockopen("example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "Ошибка: $errstr ($errno)";
} else {
    $out = "GET / HTTP/1.1\r\n";
    $out .= "Host: example.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}
HTTP/1.1 200 OK
Date: Mon, 23 Sep 2024 12:00:00 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8

<!doctype html>
<html>
<head><title>Example Domain</title></head>
<body>...</body>
</html>
Подключение с флагами
$context = stream_context_create([
    'socket' => ['tcp_nodelay' => true]
]);
$fp = stream_socket_client("tcp://example.com:80", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
if ($fp) {
    echo "Соединение установлено";
    fclose($fp);
}
Соединение установлено

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

stream_socket_client

Более современная альтернатива с поддержкой контекстов потоков. Рекомендуется для новых проектов.

cURL

Библиотека для сложных HTTP-запросов с поддержкой cookies, сессий и SSL. Предпочтительна для работы с веб-API.

socket_create

Функции модуля Socket предоставляют более низкоуровневый контроль над сокетами.

file_get_contents с HTTP-контекстом

Упрощенный способ получения данных по HTTP без необходимости ручного формирования запросов.

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

Python: socket.socket
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("example.com", 80))
s.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
print(s.recv(1024).decode())
s.close()
JavaScript: Net.Socket в Node.js
const net = require('net');
const client = net.createConnection({ port: 80, host: 'example.com' }, () => {
    client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n');
});
client.on('data', (data) => console.log(data.toString()));
Go: net.Dial
package main
import (
    "fmt"
    "net"
    "io"
)
func main() {
    conn, err := net.Dial("tcp", "example.com:80")
    if err != nil { panic(err) }
    fmt.Fprintf(conn, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
    io.Copy(os.Stdout, conn)
    conn.Close()
}

Распространенные ошибки

Таймаут соединения
$fp = fsockopen("nonexistent-domain.xyz", 80, $errno, $errstr, 2);
if (!$fp) {
    echo "Ошибка $errno: $errstr";
}
Ошибка 110: Connection timed out
Неверный порт
$fp = fsockopen("example.com", 99999, $errno, $errstr, 5);
echo $errstr;
Cannot assign requested address
Проблемы с SSL
$fp = fsockopen("ssl://example.com", 443, $errno, $errstr, 10);
if (!$fp) echo "SSL ошибка: $errstr";
SSL ошибка: Unable to enable crypto

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

PHP 8.0

Усилена строгая типизация параметров. Аргументы error_code и error_string теперь всегда передаются по ссылке.

PHP 7.0+

Добавлена поддержка IPv6-адресов в стандартном формате. Улучшена обработка таймаутов.

Будущие изменения

В следующих версиях планируется улучшение поддержки асинхронных операций.

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

Работа с UDP-протоколом
Пример php
$socket = fsockopen("udp://pool.ntp.org", 123, $errno, $errstr);
if ($socket) {
    fwrite($socket, "\x1b".str_repeat("\0", 47));
    $response = fread($socket, 48);
    $unpack = unpack('N12', $response);
    $timestamp = sprintf('%u', $unpack[9]) - 2208988800;
    echo date('Y-m-d H:i:s', $timestamp);
    fclose($socket);
}
SMTP-клиент
Пример php
$smtp = fsockopen("tcp://smtp.example.com", 25, $errno, $errstr, 30);
if ($smtp) {
    echo fgets($smtp); // Приветствие сервера
    fwrite($smtp, "HELO localhost\r\n");
    echo fgets($smtp); // Ответ на HELO
    fwrite($smtp, "QUIT\r\n");
    fclose($smtp);
}
Чтение заголовков HTTP
Пример php
$fp = fsockopen("example.com", 80);
fwrite($fp, "HEAD / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
$headers = '';
while (!feof($fp) && $headers .= fgets($fp)) {
    if (preg_match('/\r\n\r\n$/', $headers)) break;
}
preg_match('/Server: (.+)/', $headers, $matches);
echo $matches[1] ?? 'Неизвестный сервер';
fclose($fp);
Мониторинг доступности порта
Пример php
function checkPort($host, $port, $timeout = 5) {
    $fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
    if ($fp) {
        fclose($fp);
        return "Порт $port открыт";
    }
    return "Порт $port закрыт: $errstr";
}
echo checkPort('example.com', 80);
Порт 80 открыт

PHP fsockopen function comments

En
Fsockopen Open Internet or Unix domain socket connection