如何使用libcurl编写HTTP GET请求封装函数并获取响应头和响应体
修正方案与代码实现
原有代码的核心问题
- 选项用错:
CURLOPT_WRITEHEADER是用来给头回调传递自定义userdata指针的,设置头回调函数应该用CURLOPT_HEADERFUNCTION,之前把回调函数错传给了CURLOPT_WRITEHEADER,逻辑完全错位 - 回调参数理解错误:所有libcurl的写入回调参数含义统一:
buffer:libcurl收到的临时数据块,回调结束后会被回收,不能直接赋值保存指针,必须拷贝内容size * nitems:本次收到的数据块总字节长度userdata:就是提前通过对应选项传入的自定义指针,这里就是你的http_response_t结构体指针
- 回调返回值错误:必须返回实际处理的字节数(一般就是
size * nitems),如果返回值小于这个数,libcurl会直接终止请求,绝对不能返回结构体指针 - 缺少动态内存处理:响应体和响应头都是分段接收的,需要动态扩容存储,不能直接赋值临时buffer
- 缺少状态码获取逻辑:需要调用
curl_easy_getinfo获取HTTP状态码填充结构体
修正后的完整代码
首先调整结构体定义,增加头计数和响应体长度字段:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <curl/curl.h> #define MAX_HEADERS 100 typedef struct { char* key; char* value; } http_header_t; typedef struct { int status; char *body; size_t body_len; http_header_t headers[MAX_HEADERS]; int header_count; } http_response_t;
补全响应体写入回调:
size_t http_response_write(void *ptr, size_t size, size_t nmemb, void* userdata) { size_t real_size = size * nmemb; http_response_t *resp = (http_response_t*)userdata; // 动态扩容响应体内存 char *new_body = realloc(resp->body, resp->body_len + real_size + 1); if (!new_body) return 0; // 内存分配失败终止请求 resp->body = new_body; memcpy(resp->body + resp->body_len, ptr, real_size); resp->body_len += real_size; resp->body[resp->body_len] = '\0'; // 字符串结尾补0方便使用 return real_size; }
补全响应头写入回调:
size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { size_t real_size = size * nitems; http_response_t *resp = (http_response_t*)userdata; // 过滤空行(头结束的标记) if (real_size <= 2 || resp->header_count >= MAX_HEADERS) return real_size; // 拆分key和value,头的格式是 Key: Value\r\n char *colon = strchr(buffer, ':'); if (!colon) return real_size; // 拷贝key int key_len = colon - buffer; resp->headers[resp->header_count].key = malloc(key_len + 1); strncpy(resp->headers[resp->header_count].key, buffer, key_len); resp->headers[resp->header_count].key[key_len] = '\0'; // 跳过冒号和前面的空格拷贝value char *val_start = colon + 1; while (*val_start == ' ' || *val_start == '\t') val_start++; char *val_end = buffer + real_size - 2; // 去掉末尾的\r\n int val_len = val_end - val_start + 1; resp->headers[resp->header_count].value = malloc(val_len + 1); strncpy(resp->headers[resp->header_count].value, val_start, val_len); resp->headers[resp->header_count].value[val_len] = '\0'; resp->header_count++; return real_size; }
修正后的http_get函数:
void http_get(http_response_t *response, const char *url) { // 先初始化传入的response结构体,避免野指针 memset(response, 0, sizeof(http_response_t)); CURL *curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url); // 正确设置头回调和对应的userdata curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, response); // 设置响应体回调和对应的userdata curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_response_write); curl_easy_setopt(curl, CURLOPT_WRITEDATA, response); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); CURLcode res = curl_easy_perform(curl); if (res == CURLE_OK) { // 获取HTTP状态码 long status_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); response->status = (int)status_code; } curl_easy_cleanup(curl); } }
使用注意事项
- 调用
http_get前不需要手动初始化http_response_t,函数内部已经做了memset清零 - 使用完响应结果后必须手动释放内存,避免泄漏:
void free_http_response(http_response_t *resp) { if (resp->body) free(resp->body); for (int i = 0; i < resp->header_count; i++) { free(resp->headers[i].key); free(resp->headers[i].value); } memset(resp, 0, sizeof(http_response_t)); }
内容的提问来源于stack exchange,提问作者The epic face 007
相关产品推荐
相关产品推荐

