Введение: зачем писать HTTP клиент на C++?
В современной разработке взаимодействие с удалёнными серверами — одна из ключевых задач. Будь то получение данных с REST API, отправка формы на сервер или загрузка файла — везде используется протокол HTTP. Несмотря на то, что Python и JavaScript предлагают простые встроенные решения, C++ остаётся востребованным для написания высокопроизводительных HTTP клиентов. Это необходимо для встраиваемых систем, игровых движков, высоконагруженных бэкендов или просто для изучения низкоуровневых механизмов сети.
В этой статье мы разберём два популярных подхода к реализации HTTP клиента на C++: использование библиотеки libcurl (cURL) и более современного асинхронного подхода с Boost.Beast. Вы узнаете, как правильно формировать GET и POST запросы, обрабатывать ответы и избегать типичных ошибок.
1. Классический подход: HTTP клиент на C++ с libcurl
Библиотека libcurl — это проверенный временем инструмент для передачи данных по различным протоколам. Она поддерживает HTTP/1.1, HTTP/2, HTTPS, аутентификацию, куки и многое другое. Для C++ существует удобная обёртка curl/curl.h, которая позволяет легко интегрировать cURL в ваш проект.
Установка libcurl
В большинстве дистрибутивов Linux cURL уже установлен. Для разработки потребуется пакет разработчика:
sudo apt-get install libcurl4-openssl-dev # Ubuntu/Debian
brew install curl # macOS
Пример: GET запрос
Напишем простой HTTP клиент на C++, который получает данные с публичного API (например, JSONPlaceholder).
#include <iostream>
#include <string>
#include <curl/curl.h>
// Callback для записи данных в std::string
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
int main() {
CURL *curl;
CURLcode res;
std::string readBuffer;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts/1");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
// Выполняем запрос
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
} else {
std::cout << "Ответ сервера:\n" << readBuffer << std::endl;
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
Пример: POST запрос с JSON
Теперь отправим данные на сервер. Для этого нужно указать тип контента и тело запроса.
#include <iostream>
#include <string>
#include <curl/curl.h>
int main() {
CURL *curl;
CURLcode res;
std::string response;
// JSON данные для отправки
std::string jsonData = R"({"title": "foo", "body": "bar", "userId": 1})";
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());
// Устанавливаем заголовки
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// Callback для записи ответа
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, [](void *contents, size_t size, size_t nmemb, void *userp) -> size_t {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
});
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
std::cerr << "Ошибка POST: " << curl_easy_strerror(res) << std::endl;
} else {
std::cout << "Ответ: " << response << std::endl;
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
2. Современный подход: Boost.Beast для HTTP клиента на C++
Библиотека Boost.Beast — это часть экосистемы Boost, которая предоставляет асинхронные возможности для работы с HTTP и WebSocket. Она построена поверх Boost.Asio и идеально подходит для создания высокопроизводительных сетевых приложений. Если вам нужен HTTP клиент на C++ с неблокирующими запросами — это ваш выбор.
Установка Boost.Beast
Boost.Beast — это header-only библиотека (в основном), но для работы требуется Boost.Asio. Установите Boost:
sudo apt-get install libboost-all-dev # Ubuntu/Debian
brew install boost # macOS
Пример: синхронный GET запрос
Начнём с простого синхронного примера. Обратите внимание, что код требует C++17 или выше.
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <iostream>
#include <string>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
int main() {
try {
std::string host = "jsonplaceholder.typicode.com";
std::string target = "/posts/1";
int version = 11; // HTTP/1.1
net::io_context ioc;
tcp::resolver resolver(ioc);
beast::tcp_stream stream(ioc);
// Резолвим адрес и подключаемся
auto const results = resolver.resolve(host, "80");
stream.connect(results);
// Формируем GET запрос
http::