HTTP клиент на C++: делаем запросы правильно

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: зачем писать 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::                

Блоги

Рекомендаций по книгам