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

如何用C语言拼接分片HTTP请求并解析为完整JSON?

HTTP Chunked编码解析及JSON拼接实现(适配jansson库)

核心逻辑

  1. 先定位并跳过HTTP响应头:通过\r\n\r\n分隔符找到chunked数据的起始位置
  2. 逐段解析分片:读取十六进制格式的分片长度,提取对应长度的内容,跳过分片后的\r\n
  3. 拼接所有分片内容得到无冗余的完整JSON字符串
  4. 将拼接结果直接传入jansson库进行解析

完整实现代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jansson.h>

// 解析状态枚举
typedef enum {
    PARSE_HEADERS,
    PARSE_CHUNK_LEN,
    PARSE_CHUNK_DATA,
    PARSE_CHUNK_END,
    PARSE_COMPLETE
} ParseState;

// 解析上下文结构体
typedef struct {
    ParseState state;
    char *buffer;
    size_t buffer_len;
    size_t buffer_pos;
    char *json_data;
    size_t json_len;
    size_t current_chunk_len; // 当前待处理的分片长度
} ChunkParser;

// 初始化解析器
ChunkParser* chunk_parser_init() {
    ChunkParser *parser = malloc(sizeof(ChunkParser));
    if (!parser) return NULL;
    parser->state = PARSE_HEADERS;
    parser->buffer = NULL;
    parser->buffer_len = 0;
    parser->buffer_pos = 0;
    parser->json_data = NULL;
    parser->json_len = 0;
    parser->current_chunk_len = 0;
    return parser;
}

// 清理解析器资源
void chunk_parser_cleanup(ChunkParser *parser) {
    if (parser) {
        free(parser->buffer);
        free(parser->json_data);
        free(parser);
    }
}

// 喂入待解析数据,返回值:0=未完成,1=解析完成,-1=错误
int chunk_parser_feed(ChunkParser *parser, const char *input, size_t input_len) {
    // 扩展缓冲区并追加新数据
    char *new_buffer = realloc(parser->buffer, parser->buffer_len + input_len);
    if (!new_buffer) return -1;
    memcpy(new_buffer + parser->buffer_len, input, input_len);
    parser->buffer = new_buffer;
    parser->buffer_len += input_len;

    while (parser->buffer_pos < parser->buffer_len) {
        switch (parser->state) {
            case PARSE_HEADERS: {
                char *header_end = strstr(parser->buffer + parser->buffer_pos, "\r\n\r\n");
                if (!header_end) return 0; // 未收到完整响应头,等待后续数据
                parser->buffer_pos = header_end - parser->buffer + 4;
                parser->state = PARSE_CHUNK_LEN;
                break;
            }

            case PARSE_CHUNK_LEN: {
                char *len_end = strstr(parser->buffer + parser->buffer_pos, "\r\n");
                if (!len_end) return 0; // 未收到完整分片长度,等待后续数据
                
                char len_str[32];
                size_t len_str_len = len_end - (parser->buffer + parser->buffer_pos);
                if (len_str_len >= sizeof(len_str)) return -1;
                memcpy(len_str, parser->buffer + parser->buffer_pos, len_str_len);
                len_str[len_str_len] = '\0';
                unsigned long chunk_len = strtoul(len_str, NULL, 16);

                parser->buffer_pos = len_end - parser->buffer + 2;
                if (chunk_len == 0) {
                    parser->state = PARSE_COMPLETE;
                    return 1; // 解析完成
                }
                parser->current_chunk_len = chunk_len;
                parser->state = PARSE_CHUNK_DATA;
                break;
            }

            case PARSE_CHUNK_DATA: {
                if (parser->buffer_pos + parser->current_chunk_len > parser->buffer_len) {
                    return 0; // 分片数据不完整,等待后续数据
                }
                // 拼接分片数据到JSON缓冲区
                char *new_json = realloc(parser->json_data, parser->json_len + parser->current_chunk_len);
                if (!new_json) return -1;
                memcpy(new_json + parser->json_len, parser->buffer + parser->buffer_pos, parser->current_chunk_len);
                parser->json_data = new_json;
                parser->json_len += parser->current_chunk_len;

                parser->buffer_pos += parser->current_chunk_len;
                parser->state = PARSE_CHUNK_END;
                break;
            }

            case PARSE_CHUNK_END: {
                if (parser->buffer_pos + 2 > parser->buffer_len) return 0;
                if (memcmp(parser->buffer + parser->buffer_pos, "\r\n", 2) != 0) return -1; // 格式错误
                parser->buffer_pos += 2;
                parser->state = PARSE_CHUNK_LEN;
                break;
            }

            case PARSE_COMPLETE: {
                return 1;
            }
        }
    }
    return 0;
}

// 使用示例
int main() {
    // 模拟分批次收到的chunked数据
    const char *chunk1 = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n";
    const char *chunk2 = "b\r\n{\"dataChun";
    const char *chunk3 = "k\": \"qwer";
    const char *chunk4 = "ty\", \"status";
    const char *chunk5 = "\": \"ok\"}\r\n0\r\n\r\n";

    ChunkParser *parser = chunk_parser_init();
    if (!parser) {
        fprintf(stderr, "解析器初始化失败\n");
        return 1;
    }

    // 分批次喂入数据
    chunk_parser_feed(parser, chunk1, strlen(chunk1));
    chunk_parser_feed(parser, chunk2, strlen(chunk2));
    chunk_parser_feed(parser, chunk3, strlen(chunk3));
    chunk_parser_feed(parser, chunk4, strlen(chunk4));
    int res = chunk_parser_feed(parser, chunk5, strlen(chunk5));

    if (res == 1) {
        // 添加字符串结束符
        char *complete_json = realloc(parser->json_data, parser->json_len + 1);
        if (complete_json) {
            complete_json[parser->json_len] = '\0';
            parser->json_data = complete_json;

            // 用jansson解析JSON
            json_t *root = json_loads(parser->json_data, 0, NULL);
            if (root) {
                printf("JSON解析成功:\n");
                char *json_str = json_dumps(root, JSON_INDENT(2));
                printf("%s\n", json_str);
                free(json_str);
                json_decref(root);
            } else {
                fprintf(stderr, "JSON解析失败\n");
            }
        }
    } else {
        fprintf(stderr, "解析未完成或出现错误\n");
    }

    chunk_parser_cleanup(parser);
    return 0;
}

注意事项

  • 代码自动过滤HTTP响应头,仅保留分片传输的纯JSON内容,无冗余字节
  • 支持任意分批次接收的数据,只要数据完整即可正确拼接
  • 编译时需要链接jansson库:gcc your_code.c -o your_program -ljansson

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:55:02