如何用C语言拼接分片HTTP请求并解析为完整JSON?
HTTP Chunked编码解析及JSON拼接实现(适配jansson库)
核心逻辑
- 先定位并跳过HTTP响应头:通过
\r\n\r\n分隔符找到chunked数据的起始位置 - 逐段解析分片:读取十六进制格式的分片长度,提取对应长度的内容,跳过分片后的
\r\n - 拼接所有分片内容得到无冗余的完整JSON字符串
- 将拼接结果直接传入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
相关产品推荐
相关产品推荐

