Введение: зачем писать 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 всегда одинакова:
- Инициализируем глобальный контекст вызовом
curl_global_init() - Создаём дескриптор сессии
curl_easy_init() - Устанавливаем параметры запроса (URL, заголовки, метод и т.д.)
- Выполняем запрос
curl_easy_perform() - Очищаем ресурсы
Звучит просто? Давайте перейдём к коду.
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:
<