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

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

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

Начать курс

Введение: зачем писать HTTP-клиент на C++?

В современной разработке взаимодействие с веб-сервисами стало неотъемлемой частью практически любого приложения. Даже если вы пишете десктопную программу на C++, рано или поздно вам потребуется отправить данные на сервер или получить их оттуда. В отличие от скриптовых языков, C++ не имеет встроенных средств для работы с HTTP — но это не проблема. Существует несколько проверенных способов реализовать HTTP-клиент на C++, и мы разберём их от простого к сложному.

В этой статье мы рассмотрим:

  • Как использовать библиотеку libcurl — золотой стандарт для работы с HTTP в C++
  • Как отправить GET и POST запросы с заголовками и телом
  • Как обработать полученный ответ и распарсить JSON
  • Как избежать типичных ошибок при работе с сетевыми запросами

Все примеры кода можно сразу использовать в своих проектах. Поехали!

1. Выбор библиотеки: libcurl как основной инструмент

Написание HTTP-клиента на C++ с нуля — задача неблагодарная. Вам придётся работать с сокетами, вручную формировать заголовки, парсить ответы и обрабатывать кучу краевых случаев. К счастью, существует libcurl — мощная, стабильная и кроссплатформенная библиотека, которая делает всю грязную работу за вас.

Чтобы начать, установите libcurl. На Ubuntu это делается одной командой:

sudo apt-get install libcurl4-openssl-dev

На Windows можно воспользоваться vcpkg или скачать готовые бинарники. После установки подключите библиотеку в своём CMakeLists.txt или просто линкуйте вручную с флагами -lcurl.

Базовая структура работы с libcurl всегда одинакова:

  1. Инициализируем глобальный контекст вызовом curl_global_init()
  2. Создаём дескриптор сессии curl_easy_init()
  3. Устанавливаем параметры запроса (URL, заголовки, метод и т.д.)
  4. Выполняем запрос curl_easy_perform()
  5. Очищаем ресурсы

Звучит просто? Давайте перейдём к коду.

2. Отправка GET и POST запросов: практические примеры

2.1 Простой GET-запрос

Начнём с классики — получим данные с публичного API. Предположим, мы хотим узнать текущий курс Bitcoin к доллару:

#include <iostream>
#include <string>
#include <curl/curl.h>

// Callback-функция для записи данных в строку
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_global_init(CURL_GLOBAL_DEFAULT);
    CURL* curl = curl_easy_init();
    std::string response;
    
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "https://api.coindesk.com/v1/bpi/currentprice.json");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            std::cerr << "Ошибка: " << curl_easy_strerror(res) << std::endl;
        } else {
            std::cout << "Ответ сервера:\n" << response << std::endl;
        }
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    return 0;
}

Обратите внимание на callback-функцию WriteCallback. libcurl не знает, куда сохранять данные, поэтому мы передаём указатель на строку и пишем в неё по мере поступления данных. Это стандартный паттерн.

2.2 POST-запрос с JSON-телом

Теперь отправим данные на сервер. Допустим, мы регистрируем пользователя:

#include <iostream>
#include <string>
#include <curl/curl.h>

int main() {
    curl_global_init(CURL_GLOBAL_DEFAULT);
    CURL* curl = curl_easy_init();
    std::string response;
    
    if (curl) {
        // Настройка URL
        curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts");
        
        // Формируем JSON-тело
        std::string json_data = R"({"title":"foo","body":"bar","userId":1})";
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data.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, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            std::cerr << "Ошибка POST: " << curl_easy_strerror(res) << std::endl;
        } else {
            std::cout << "Ответ:\n" << response << std::endl;
        }
        
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    return 0;
}

Здесь мы добавили ручную установку заголовка Content-Type. Без него сервер может не понять, что ему пришёл JSON. Также обратите внимание на CURLOPT_POSTFIELDS — он автоматически переключает метод на POST.

3. Парсинг ответа: работаем с JSON в C++

Получить ответ от сервера — только половина дела. Чаще всего данные приходят в формате JSON, и их нужно разобрать. Для этого в C++ используют библиотеки nlohmann/json (рекомендуется) или RapidJSON.

Установим nlohmann/json (одна заголовочная библиотека):

#include <nlohmann/json.hpp>
using json = nlohmann::json;

Теперь распарсим ответ от нашего GET-запроса с курсом Bitcoin:

<                

Блоги

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