报文结构化解析

一、代码功能解析

  1. 核心功能
    实现基于状态机的环形缓冲区帧解析器,支持协议格式:帧头(N字节) + 长度(1字节) + 数据载荷。该设计符合常见通信协议解析模式。

  2. 状态机流程

switch (framehead_code->chk_step) {
case 0: // 帧头校验(含滑动对齐)
case 1: // 长度字段提取(含字节序转换)
case 2: // 有效载荷读取(边界检查)

采用三阶段状态机,与WebSocket帧解析的FIN-OPCODE-MASK-PAYLOAD结构类似。

二、关键技术点

  1. 帧头校验机制
// 滑动窗口实现(类似TCP协议)
memmove(&ptr[0], &ptr[1], framehead_code->loaded_length);

通过memmove实现字节滑动对齐,最多重试framehead_mem次,该策略可防止无效数据阻塞缓冲区。

  1. 长度字段处理
// 大端/小端转换
if (framehead_code->egg_type == framelen_bigegg) 
    mx_lebeswap((uint8_t *)&frame_len, ...);

支持1/2/4字节长度字段,类似WebSocket的Payload Length扩展机制。

  1. 缓冲区安全
if (frame_len > size) frame_len = size; // 防溢出截断

强制限制输出缓冲区长度,避免内存越界,该防护措施在嵌入式开发中尤为重要3

/**
 * @file    frame_parser.c
 * @brief   基于环形缓冲区的协议帧解析器(协议格式:帧头+长度+数据)
 * @version 2.1
 * @date    2025-03-19
 * 
 * @par 协议帧结构
 * +----------------+---------------+----------------+
 * | 帧头(N字节)     | 长度(1字节)    | 数据载荷(N字节) |
 * +----------------+---------------+----------------+
 * 
 * @warning 调用前需确保ptr缓冲区大小≥最大可能帧长度
 */

/* 状态机步骤枚举 */
typedef enum {
    PARSE_STEP_HEADER,   ///< 帧头解析阶段(含滑动对齐机制)
    PARSE_STEP_LENGTH,   ///< 长度字段提取阶段
    PARSE_STEP_PAYLOAD   ///< 有效载荷获取阶段
} ParseStep;

/**
 * @brief       从环形缓冲区提取完整数据帧
 * @param[in]   mx            环形缓冲区对象(采用循环缓冲区设计[1](@ref))
 * @param[in]   frame_config  帧配置参数(含校验函数/长度偏移等)
 * @param[out]  output_buf    输出缓冲区地址(需预分配足够空间)
 * @param[in]   max_size      输出缓冲区最大容量
 * @return      成功解析的帧字节数(0表示数据不足或校验失败)
 * 
 * @note 状态机流程:
 * 1. HEADER阶段:通过memmove实现滑动窗口检测帧头[1](@ref),最多重试5次
 * 2. LENGTH阶段:解析长度字段(支持大端/小端格式)
 * 3. PAYLOAD阶段:批量读取数据载荷(采用内存预分配策略[1](@ref))
 * 
 * @warning 非线程安全,多线程环境需外部同步
 */
uint32_t mx_chk_ringbuff_frame(void *mx, 
                              FrameConfig *frame_config,
                              uint8_t *output_buf,
                              uint32_t max_size)
{
    /* 初始化局部变量缓存配置参数(减少指针解引用) */
    const uint8_t header_len = frame_config->header_size;
    uint8_t retry_count = 0;
    
    //=============================================
    // 阶段1:帧头检测与对齐(含滑动窗口机制)
    //=============================================
    while (frame_config->parse_step == PARSE_STEP_HEADER) 
    {
        /* 计算环形缓冲区可用数据量(避免重复调用API) */
        uint32_t available_data = mx_ringbuffer_available(mx);
        
        /* 检查最小数据量要求:已加载长度 + 剩余长度 ≥ 帧头长度 */
        if ((frame_config->loaded_bytes + available_data) < header_len) {
            frame_config->retry_count = 0;
            return 0; // 数据不足直接返回[1](@ref)
        }

        /* 填充帧头缓冲区(采用分批加载策略减少内存拷贝) */
        mx_ringbuffer_read(mx, 
                          output_buf + frame_config->loaded_bytes,
                          header_len - frame_config->loaded_bytes);

        /* 执行自定义帧头校验(支持多模式匹配) */
        if (frame_config->header_validator(output_buf, 
                                         frame_config->header_pattern, 
                                         header_len)) 
        {
            frame_config->parse_step = PARSE_STEP_LENGTH;
            break; // 校验成功进入下一阶段
        }

        /* 帧头失配处理:滑动窗口右移1字节(内存高效管理[1](@ref)) */
        memmove(output_buf, output_buf + 1, --frame_config->loaded_bytes);
        if (++retry_count > MAX_HEADER_RETRIES) {
            log_warn("Header validation failed after %d attempts", retry_count);
            return 0; // 超过最大重试次数终止
        }
    }

    //=============================================
    // 阶段2:解析长度字段(含字节序转换)
    //=============================================
    if (frame_config->parse_step == PARSE_STEP_LENGTH) 
    {
        /* 计算需要读取的字节数(含长度字段和保留位) */
        const uint8_t len_field_size = 1 + frame_config->reserved_bytes;
        const uint32_t expect_len = header_len + len_field_size;

        /* 批量读取长度字段(减少多次小数据量读取) */
        mx_ringbuffer_read(mx, 
                          output_buf + header_len, 
                          expect_len - frame_config->loaded_bytes);

        /* 解析长度值(支持大端格式转换) */
        uint32_t payload_len = 0;
        memcpy(&payload_len, 
              output_buf + header_len - frame_config->len_offset,
              len_field_size);
        if (frame_config->is_big_endian) {
            payload_len = ntohl(payload_len); // 网络字节序转换
        }

        /* 检查输出缓冲区容量限制(防止内存溢出[1](@ref)) */
        if ((header_len + payload_len) > max_size) {
            log_error("Frame size %u exceeds buffer capacity %u", 
                     header_len + payload_len, max_size);
            return 0; 
        }
        
        frame_config->parse_step = PARSE_STEP_PAYLOAD;
    }

    //=============================================
    // 阶段3:读取有效载荷(批量处理优化)
    //=============================================
    if (frame_config->parse_step == PARSE_STEP_PAYLOAD) 
    {
        /* 计算剩余需要读取的字节数 */
        const uint32_t total_frame_len = header_len + payload_len;
        const uint32_t remaining = total_frame_len - frame_config->loaded_bytes;

        /* 批量读取数据载荷(最大化读取效率[1](@ref)) */
        mx_ringbuffer_read(mx, 
                          output_buf + frame_config->loaded_bytes, 
                          remaining);

        /* 状态机重置为初始状态 */
        frame_config->parse_step = PARSE_STEP_HEADER;
        frame_config->loaded_bytes = 0;
        return total_frame_len; // 返回成功解析的帧长度
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值