Введение: почему сетевое программирование на PHP — это не страшно, а интересно
Когда слышишь «сетевое программирование», в голове сразу всплывают образы суровых C++-разработчиков, колдующих над низкоуровневыми сокетами, или админов, пишущих скрипты на Python для мониторинга сети. А PHP? Разве его удел — не просто выводить «Hello, World!» на сайте?
На самом деле, PHP — это мощный инструмент для сетевого взаимодействия. Он умеет открывать сокеты, ходить по HTTP, работать с WebSocket и даже парсить чужие сайты. И всё это — без установки дополнительных расширений (ну, почти).
В этой статье мы разберём ключевые техники сетевого программирования на PHP. Вы узнаете, как написать простой TCP-клиент, как автоматизировать запросы к API, как парсить сайты и как создать свой чат на WebSocket. Поехали!
1. Основы сокетов в PHP
Сокеты — это низкоуровневый способ общения между программами по сети. В PHP работа с сокетами реализована через функции семейства socket_*. Они есть в стандартной сборке, но иногда требуют включения расширения sockets в php.ini.
Создание TCP-клиента
Напишем простой клиент, который подключается к серверу (например, к time.nist.gov — серверу точного времени) и читает ответ:
<?php
$host = 'time.nist.gov';
$port = 37;
// Создаём сокет
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
echo "Не удалось создать сокет: " . socket_strerror(socket_last_error()) . "\n";
exit;
}
// Подключаемся
$result = socket_connect($socket, $host, $port);
if ($result === false) {
echo "Не удалось подключиться: " . socket_strerror(socket_last_error($socket)) . "\n";
exit;
}
// Читаем 4 байта (время в формате NTP)
$bytes = socket_read($socket, 4);
if ($bytes === false) {
echo "Ошибка чтения: " . socket_strerror(socket_last_error($socket)) . "\n";
exit;
}
// Закрываем сокет
socket_close($socket);
// Преобразуем бинарные данные в число
$time = unpack('N', $bytes)[1];
// NTP-время отсчитывается от 1900 года, отнимаем 70 лет (секунды)
$unix_time = $time - 2208988800;
echo "Текущее время (UNIX): " . date('Y-m-d H:i:s', $unix_time) . "\n";
?>
Этот код соединяется с сервером точного времени, читает 4 байта (время в формате NTP) и преобразует их в понятную дату. Обратите внимание на обработку ошибок — в сетевом программировании это критично.
Создание простого TCP-сервера
А теперь напишем свой мини-сервер, который будет принимать соединения и отвечать «Hello, client!»:
<?php
$host = '127.0.0.1';
$port = 8080;
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, $host, $port);
socket_listen($server, 5);
echo "Сервер запущен на $host:$port\n";
while (true) {
$client = socket_accept($server);
if ($client === false) {
continue;
}
$msg = "Hello, client!\n";
socket_write($client, $msg, strlen($msg));
socket_close($client);
}
socket_close($server);
?>
Запустите скрипт в консоли (php server.php), а затем подключитесь к нему через telnet: telnet 127.0.0.1 8080. Увидите приветствие.
Совет: Для реальных проектов используйте готовые библиотеки вроде ReactPHP или Amp — они дают асинхронность и удобный API.
2. HTTP-запросы с cURL
cURL — это швейцарский нож для работы с HTTP в PHP. Он умеет делать GET, POST, работать с куками, заголовками, прокси и сертификатами. Расширение curl обычно предустановлено.
Базовый GET-запрос
<?php
$ch = curl_init('https://api.github.com/repos/php/php-src');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHP Script');
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code === 200) {
$data = json_decode($response, true);
echo "Репозиторий: " . $data['full_name'] . "\n";
echo "Звёзд: " . $data['stargazers_count'] . "\n";
} else {
echo "Ошибка: HTTP $http_code\n";
}
?>
Обратите внимание: мы устанавливаем User-Agent — многие API (например, GitHub) блокируют запросы без этого заголовка.
POST-запрос с отправкой JSON
<?php
$ch = curl_init('https://jsonplaceholder.typicode.com/posts');
$payload = json_encode([
'title' => 'Сетевое программирование на PHP',
'body' => 'Это тестовый пост',
'userId' => 1
]);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Content-Length: ' . strlen($payload)
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code === 201) {
$data = json_decode($response, true);
echo "Создан пост с ID: " . $data['id'] . "\n";
}
?>
Работа с прокси и таймаутами
Если вам нужно парсить сайты через прокси или ставить ограничения по времени:
<?php
$ch = curl_init('https://httpbin.org/ip');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_PROXY => 'http://user:pass@proxy.example.com:3128',
CURLOPT_TIMEOUT => 10, // секунд
CURLOPT_CONNECTTIMEOUT => 5
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
3. Парсинг сайтов (веб-скрапинг)
Парсинг — одна из самых популярных задач сетевого программирования. PHP справляется с ней отлично, особенно в связке c cURL и библиотеками для работы с HTML.
Важно: Всегда проверяйте файл robots.txt и соблюдайте законы об авторских правах. Не парсьте сайты, которые это запрещают.
Парсинг с помощью регулярных выражений
Самый простой (но хрупкий) способ — использовать preg_match:
<