libcurl 是功能最全、移植性最好、性能最強的開源 URL 传輸庫,支持 HTTP/HTTPS/FTP/SMTP 等 20+ 協議。本文基於 libcurl Easy API,構建一個完整的 HTTP 客戶端,演示從初始化、配置、執行、回調處理到清理的全流程。
CURLOPT_WRITEFUNCTION當 libcurl 接收到數據時,會多次調用此回調,將數據塊傳遞給應用層。應用層需負責緩衝區管理 (擴容、拷貝、終止符)。
#include#include #include #include #define MAX_BUF 65536 // 64KB 初始緩衝區 // 動態緩衝區上下文 struct WriteContext { char *buffer; size_t size; // 當前分配大小 size_t len; // 已用長度 int error; // 錯誤標誌 }; // 寫入回調:數據到達時調用 static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct WriteContext *ctx = (struct WriteContext*)userp; // 1. 擴容檢查 (指數增長策略) if (ctx->len + realsize >= ctx->size) { size_t new_size = ctx->size ? ctx->size * 2 : MAX_BUF; while (ctx->len + realsize >= new_size) new_size *= 2; char *new_buf = realloc(ctx->buffer, new_size); if (!new_buf) { ctx->error = 1; return 0; } // 返回 0 通知 curl 中止 ctx->buffer = new_buf; ctx->size = new_size; } // 2. 拷貝數據 memcpy(ctx->buffer + ctx->len, contents, realsize); ctx->len += realsize; ctx->buffer[ctx->len] = '\0'; // 字符串終止 (便於直接 printf) return realsize; // 返回處理字節數,告知 curl 成功 } int main(void) { CURL *curl; CURLcode res; struct WriteContext ctx = {0}; // 1. 初始化 curl = curl_easy_init(); if (!curl) { fprintf(stderr, "curl_easy_init 失敗\n"); return 1; } // 2. 配置 URL curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com"); // 3. 配置寫入回調與上下文 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx); // 4. 推薦配置 (生產環境必備) curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); // 總超時 10秒 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); // 連接超時 5秒 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 自動跟隨重定向 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L); // 最大重定向次數 curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); // CURLOPT_SSL_VERIFYPEER 0 僅用於測試,生產必須驗證證書 // 5. 執行傳輸 res = curl_easy_perform(curl); // 6. 結果處理 if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform 失敗: %s\n", curl_easy_strerror(res)); } else if (ctx.error) { fprintf(stderr, "內存分配失敗\n"); } else { long http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); printf("HTTP 狀態碼: %ld\n", http_code); printf("響應長度: %zu\n", ctx.len); printf("響應內容:\n%s\n", ctx.buffer); } // 7. 清理 free(ctx.buffer); curl_easy_cleanup(curl); return 0; }
# Linux/macOS gcc http_client.c -o http_client -lcurl # Windows (MinGW) gcc http_client.c -o http_client.exe -lcurl # MSVC cl http_client.c libcurl.lib
| 需求 | CURLOPT 選項 | 示例值 |
|---|---|---|
| POST 數據 | CURLOPT_POST, CURLOPT_POSTFIELDS | 1, "key=value&a=b" |
| 自定義請求頭 | CURLOPT_HTTPHEADER | struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); |
| HTTPS 驗證 | CURLOPT_CAINFO, CURLOPT_SSL_VERIFYPEER | "cacert.pem", 1L |
| 代理設置 | CURLOPT_PROXY, CURLOPT_PROXYTYPE | "https://proxy:8080", CURLPROXY_HTTP |
| 斷點續傳 | CURLOPT_RESUME_FROM_LARGE | (curl_off_t)offset |
| 多文件上傳 | CURLOPT_MIMEPOST (curl_mime_* API) | 構建 mime 結構 |
| 獲取響應頭 | CURLOPT_HEADERFUNCTION, CURLOPT_HEADERDATA | 類似 write_callback |
| Cookie 管理 | CURLOPT_COOKIEJAR, CURLOPT_COOKIEFILE | "cookies.txt" |
Easy API 是同步阻塞的。高併發 (數百上千連接) 需使用 Multi Interface:
CURLM *multi = curl_multi_init(); // 添加多個 easy handle: curl_multi_add_handle(multi, easy_handle); // 事件循環: curl_multi_perform() + curl_multi_poll() / select() // 移除完成: curl_multi_remove_handle() curl_multi_cleanup(multi);
libcurl Easy API 以「句柄 + 選項 + 回調」的靈活設計,將複雜的協議細節封裝為簡潔的 C 接口。掌握 WRITEFUNCTION 動態緩衝區管理、超時與重定向控制、SSL 驗證、Multi Interface 異步模型,即可構建生產級 HTTP 客戶端。建議閱讀 curl.h 並實戰封裝一個適合項目風格的 C++ Wrapper (RAII 管理資源、異常處理、鏈式調用)。