libcurl — HTTP 客户端
libcurl 是最广泛使用的 C/C++ HTTP 客户端库,支持 HTTP/HTTPS/FTP/SMTP 等 30+ 协议,稳定可靠,是生产级 HTTP 请求的首选。
安装
bash
# Ubuntu
sudo apt install libcurl4-openssl-dev
# vcpkg
vcpkg install curl
# CMake
find_package(CURL REQUIRED)
target_link_libraries(myapp PRIVATE CURL::libcurl)C++ RAII 封装
cpp
#include <curl/curl.h>
#include <string>
#include <stdexcept>
// 写回调:将响应数据写入 string
static size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) {
auto* buf = static_cast<std::string*>(userdata);
buf->append(ptr, size * nmemb);
return size * nmemb;
}
class CurlHandle {
CURL* curl_ = nullptr;
public:
CurlHandle() {
curl_ = curl_easy_init();
if (!curl_) throw std::runtime_error("curl_easy_init 失败");
}
~CurlHandle() { if (curl_) curl_easy_cleanup(curl_); }
CURL* get() { return curl_; }
CurlHandle(const CurlHandle&) = delete;
CurlHandle& operator=(const CurlHandle&) = delete;
};
struct Response {
int status_code;
std::string body;
std::string headers;
};
Response http_get(const std::string& url,
const std::vector<std::string>& headers = {}) {
CurlHandle curl;
Response resp;
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &resp.body);
curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, &resp.headers);
curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, 30L);
curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 1L);
// 自定义请求头
struct curl_slist* header_list = nullptr;
for (const auto& h : headers) {
header_list = curl_slist_append(header_list, h.c_str());
}
if (header_list) {
curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header_list);
}
CURLcode rc = curl_easy_perform(curl.get());
if (header_list) curl_slist_free_all(header_list);
if (rc != CURLE_OK) {
throw std::runtime_error(curl_easy_strerror(rc));
}
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &resp.status_code);
return resp;
}GET / POST / PUT / DELETE
cpp
// GET
auto resp = http_get("https://api.example.com/users",
{"Authorization: Bearer token123",
"Accept: application/json"});
std::cout << resp.status_code << "\n";
std::cout << resp.body << "\n";
// POST JSON
Response http_post(const std::string& url, const std::string& json_body) {
CurlHandle curl;
Response resp;
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
curl_easy_setopt(curl.get(), CURLOPT_POST, 1L);
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, json_body.c_str());
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, json_body.size());
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &resp.body);
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers);
CURLcode rc = curl_easy_perform(curl.get());
curl_slist_free_all(headers);
if (rc != CURLE_OK) throw std::runtime_error(curl_easy_strerror(rc));
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &resp.status_code);
return resp;
}
// 使用
nlohmann::json body = {{"name", "Alice"}, {"age", 30}};
auto r = http_post("https://api.example.com/users", body.dump());文件上传与下载
cpp
// 文件下载(带进度)
static int progress_callback(void* clientp, curl_off_t dltotal,
curl_off_t dlnow, curl_off_t, curl_off_t) {
if (dltotal > 0) {
printf("\r下载进度: %.1f%%", 100.0 * dlnow / dltotal);
fflush(stdout);
}
return 0;
}
void download_file(const std::string& url, const std::string& path) {
CurlHandle curl;
FILE* fp = fopen(path.c_str(), "wb");
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, nullptr); // 默认写到 FILE*
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl.get(), CURLOPT_XFERINFOFUNCTION, progress_callback);
curl_easy_setopt(curl.get(), CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_perform(curl.get());
fclose(fp);
printf("\n下载完成\n");
}
// multipart 文件上传
void upload_file(const std::string& url, const std::string& file_path) {
CurlHandle curl;
Response resp;
curl_mime* form = curl_mime_init(curl.get());
curl_mimepart* field = curl_mime_addpart(form);
curl_mime_name(field, "file");
curl_mime_filedata(field, file_path.c_str());
curl_mimepart* field2 = curl_mime_addpart(form);
curl_mime_name(field2, "description");
curl_mime_data(field2, "My file", CURL_ZERO_TERMINATED);
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
curl_easy_setopt(curl.get(), CURLOPT_MIMEPOST, form);
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &resp.body);
curl_easy_perform(curl.get());
curl_mime_free(form);
}连接池(multi handle)
cpp
// curl_multi:并发多个请求
CURLM* multi = curl_multi_init();
// 添加多个请求
std::vector<std::pair<CURL*, std::string>> handles;
for (const auto& url : urls) {
CURL* easy = curl_easy_init();
auto* buf = new std::string();
curl_easy_setopt(easy, CURLOPT_URL, url.c_str());
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(easy, CURLOPT_WRITEDATA, buf);
curl_multi_add_handle(multi, easy);
handles.push_back({easy, ""});
}
// 并发执行
int running = 0;
do {
curl_multi_perform(multi, &running);
curl_multi_wait(multi, nullptr, 0, 1000, nullptr);
} while (running > 0);
// 清理
for (auto& [easy, _] : handles) {
curl_multi_remove_handle(multi, easy);
curl_easy_cleanup(easy);
}
curl_multi_cleanup(multi);关键认知
libcurl 是最稳定可靠的 HTTP 客户端,支持所有主流协议和认证方式。用 RAII 包装 CURL* 句柄避免资源泄漏。并发请求用 curl_multi,比多线程更高效。生产代码必须设置超时(CURLOPT_TIMEOUT)和 SSL 验证(CURLOPT_SSL_VERIFYPEER)。