You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用cJSON_Parse()解析libcurl长响应时偶现对象损坏问题

问题分析与解决方案

核心症状梳理:

  • 调用API获取大体积响应时,在libcurl的write回调中用cJSON_Parse()解析后,cJSON_Print()偶发输出异常字符串;小体积响应无此问题
  • 直接打印回调的data参数始终能得到完整响应,开启内存检查工具(如Valgrind)后异常消失

最可能的根源与修复方案

1. 未正确拼接libcurl分块返回的响应数据

libcurl针对大体积响应会分多次触发write回调返回数据片段,如果直接用单次回调的data调用cJSON_Parse(),相当于每次只解析响应的一部分,必然得到异常的JSON结构,最终打印出奇怪内容。小响应可能刚好一次回调就返回完整数据,因此不会触发问题。

修复:

  • 在回调外部维护一个动态缓冲区,每次回调将data内容追加到缓冲区末尾
  • 等curl_easy_perform()执行完成(所有数据接收完毕),再对完整缓冲区调用cJSON_Parse()
  • 必须给缓冲区末尾添加'\0'终止符,因为libcurl返回的data不带终止符

示例代码片段:

// 定义结构体保存缓冲区和当前长度
typedef struct {
    char *buffer;
    size_t size;
} ResponseBuffer;

// libcurl write回调函数
size_t write_callback(void *data, size_t size, size_t nmemb, void *userp) {
    ResponseBuffer *resp = (ResponseBuffer *)userp;
    size_t real_size = size * nmemb;
    
    // 扩容缓冲区,预留终止符位置
    char *tmp = realloc(resp->buffer, resp->size + real_size + 1);
    if (!tmp) {
        // 内存分配失败处理,返回0告知libcurl中断请求
        return 0;
    }
    resp->buffer = tmp;
    // 追加新数据到缓冲区
    memcpy(resp->buffer + resp->size, data, real_size);
    resp->size += real_size;
    // 添加字符串终止符
    resp->buffer[resp->size] = '\0';
    
    return real_size;
}

// 主逻辑示例
int main() {
    ResponseBuffer resp = {NULL, 0};
    CURL *curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "your_target_api_url");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
        
        CURLcode res = curl_easy_perform(curl);
        if (res == CURLE_OK) {
            // 所有数据接收完成后再解析JSON
            cJSON *json = cJSON_Parse(resp.buffer);
            if (json) {
                char *print_str = cJSON_Print(json);
                if (print_str) {
                    printf("%s\n", print_str);
                    free(print_str);
                }
                cJSON_Delete(json);
            }
        }
        curl_easy_cleanup(curl);
        free(resp.buffer);
    }
    return 0;
}

2. 未添加字符串终止符导致解析异常

即使拼接了数据,若不给缓冲区加'\0',cJSON_Parse()会持续读取内存直到遇到随机的'\0',这会导致解析出错误的JSON结构,引发偶发异常。大响应更容易触发此问题,因为内存后续的随机字符更可能干扰解析。

修复:严格按照上述示例,每次追加数据后都给缓冲区设置终止符。

3. 内存越界引发的未定义行为

开启内存检查工具后异常消失,说明可能存在内存越界(比如缓冲区扩容时计算错误、使用已释放的内存)。内存检查工具会拦截这类越界操作,或让内存布局更规整,从而掩盖问题。

排查与修复:

  • 用Valgrind运行程序,查看是否有Invalid write或Invalid read的提示,定位越界位置
  • 检查缓冲区扩容的计算逻辑,确保realloc的大小是当前size + 新数据size + 1(+1用于存放终止符)
  • 确认cJSON_Delete()已正确调用,避免内存泄漏或野指针

验证步骤

  1. 先将解析逻辑从write回调移到所有数据接收完成后,验证是否解决问题
  2. 检查缓冲区是否始终以'\0'结尾
  3. 用Valgrind扫描内存问题,修复所有越界或泄漏点

内容的提问来源于stack exchange,提问作者alfredapri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 01:10:55