嵌入式软件中如何用责任链模式重构串口协议栈

使用责任链模式重构串口协议栈,是解决多协议处理、代码高耦合问题的经典方案。它能将复杂的协议解析逻辑拆解为一个个独立的、可复用的处理节点,使你的代码更清晰、更健壮、更易于扩展。

为什么要用责任链模式?

在传统的串口协议处理中,我们常常会写一个巨大的 if-else 或 switch-case 函数,里面混杂着各种协议的判断和处理逻辑。

c 复制代码
//  重构前:高度耦合的"巨型函数"
void parse_packet(uint8_t *data, int len) {
    if (check_crc(data, len)) { // 1. 校验
        if (data == 0xAA) {  // 2. 判断协议A
            // ... 处理协议A
        } else if (data == 0x55) { // 3. 判断协议B
            // ... 处理协议B
        } else if (is_modbus_frame(data, len)) { // 4. 判断Modbus
            // ... 处理Modbus
        }
        // ... 新增协议C?继续在这里加 else if
    }
}

这种方式的缺点非常明显:

高耦合: 所有协议逻辑都挤在一起,修改一个协议可能影响到其他协议。

难扩展: 每增加一个新协议,都需要修改这个核心函数,违反了"开闭原则"。

难测试: 无法单独测试某个协议的解析逻辑。

责任链模式通过"链式"传递请求,完美解决了这些问题。每个协议处理器只关心自己能否处理,不能处理就丢给下一个

责任链重构三步走:

第一步:定义统一的处理器接口

首先,我们需要一个抽象基类(在C语言中可以用结构体和函数指针模拟),作为所有协议处理器的"标准"。

c 复制代码
// handler.h - 抽象处理者
#ifndef HANDLER_H
#define HANDLER_H

#include <stdint.h>
#include <stdbool.h>

// 处理结果枚举
typedef enum {
    HANDLER_OK,       // 已成功处理,链条终止
    HANDLER_PASS,     // 无法处理,传递给下一个
    HANDLER_ERROR     // 发生错误,链条终止
} HandlerResult;

// 抽象处理者接口
typedef struct ProtocolHandler {
    // 指向下一个处理者的指针
    struct ProtocolHandler* next;
    
    // 处理请求的函数指针
    HandlerResult (*handle)(struct ProtocolHandler* self, uint8_t* data, int len);
    
    // 设置下一个处理者
    void (*set_next)(struct ProtocolHandler* self, struct ProtocolHandler* next);
} ProtocolHandler;

// 默认实现 set_next 函数
static inline void default_set_next(ProtocolHandler* self, ProtocolHandler* next) {
    self->next = next;
}

#endif // HANDLER_H

第二步:实现具体的协议处理器

接下来,为每个具体的协议(如协议A、协议B、Modbus)创建一个处理者。每个处理者都遵循统一的接口。

c 复制代码
// protocol_a_handler.c - 具体处理者A
#include "handler.h"
#include <stdio.h>

// 协议A的具体处理逻辑
static HandlerResult handle_protocol_a(ProtocolHandler* self, uint8_t* data, int len) {
    // 1. 判断是否是协议A (例如,检查帧头)
    if (len > 0 && data == 0xAA) {
        printf("Protocol A: Handling frame. Data: %02X\n", data);
        // ... 执行协议A的完整解析 ...
        return HANDLER_OK; // 处理成功,结束链条
    }
    
    // 2. 不是协议A,传递给下一个处理者
    if (self->next != NULL) {
        return self->next->handle(self->next, data, len);
    }
    
    return HANDLER_PASS; // 链尾,无人处理
}

// 创建并初始化一个协议A处理者实例
ProtocolHandler* create_protocol_a_handler() {
    static ProtocolHandler handler;
    handler.next = NULL;
    handler.handle = handle_protocol_a;
    handler.set_next = default_set_next;
    return &handler;
}
// modbus_handler.c - 具体处理者:Modbus RTU
#include "handler.h"
#include <stdio.h>

// Modbus的具体处理逻辑
static HandlerResult handle_modbus(ProtocolHandler* self, uint8_t* data, int len) {
    // 1. 判断是否是Modbus帧 (例如,检查地址和CRC)
    if (len >= 4 && check_modbus_crc(data, len)) { // 假设有此函数
        printf("Modbus RTU: Handling request. Address: %d\n", data);
        // ... 执行Modbus命令解析 ...
        return HANDLER_OK;
    }
    
    // 2. 不是Modbus,传递给下一个
    if (self->next != NULL) {
        return self->next->handle(self->next, data, len);
    }
    
    return HANDLER_PASS;
}

// 创建并初始化一个Modbus处理者实例
ProtocolHandler* create_modbus_handler() {
    static ProtocolHandler handler;
    handler.next = NULL;
    handler.handle = handle_modbus;
    handler.set_next = default_set_next;
    return &handler;
}

第三步:组装责任链并处理数据

最后,在系统初始化时,将各个处理器链接起来,形成一个完整的处理链条

c 复制代码
// main.c - 客户端代码
#include "handler.h"
#include <stdio.h>

// 声明外部创建处理者的函数
extern ProtocolHandler* create_protocol_a_handler();
extern ProtocolHandler* create_modbus_handler();

int main() {
    // 1. 创建各个处理者
    ProtocolHandler* handler_a = create_protocol_a_handler();
    ProtocolHandler* handler_modbus = create_modbus_handler();
    
    // 2. 组装责任链: A -> Modbus
    handler_a->set_next(handler_a, handler_modbus);
    
    // 现在,handler_a 就是整个责任链的入口
    ProtocolHandler* chain_head = handler_a;
    
    // 3. 模拟接收到的串口数据
    uint8_t frame_a[] = {0xAA, 0x01, 0x02};
    uint8_t frame_modbus[] = {0x01, 0x03, 0x00, 0x00, 0x44, 0xB9}; // 假设的Modbus帧
    uint8_t frame_unknown[] = {0xFF, 0xFF};
    
    printf("--- Processing Frame A ---\n");
    chain_head->handle(chain_head, frame_a, sizeof(frame_a));
    
    printf("\n--- Processing Modbus Frame ---\n");
    chain_head->handle(chain_head, frame_modbus, sizeof(frame_modbus));
    
    printf("\n--- Processing Unknown Frame ---\n");
    chain_head->handle(chain_head, frame_unknown, sizeof(frame_unknown));

    return 0;
}

重构后的优势

低耦合: 每个协议处理器都是独立的模块,互不干扰。Protocol A 处理器完全不知道 Modbus 处理器的存在。

高内聚: 每个处理者只负责一种协议的解析,逻辑清晰,代码可读性强。

易扩展: 想增加一个新协议?只需创建一个新的处理者类,实现 handle 方法,然后把它插入到责任链的任意位置即可,无需修改任何现有代码。

易复用: 一些通用处理逻辑(如CRC校验、日志记录、解密)也可以做成独立的处理者,方便地在不同的责任链中复用。

通过这种方式,你的串口协议栈将变得像一个可插拔的积木系统,灵活而强大!

相关推荐
神一样的老师41 分钟前
【兆易创新GD32VW553开发板试用】红外遥控接入天气时钟实战
驱动开发·单片机·嵌入式硬件
北方的流星1 小时前
华三路由器NAT配置
运维·网络·华三
数据法师2 小时前
开源情报收集工具GhostTrack深度测评:IP、手机号、用户名的合规信息查询方案
网络·网络协议·tcp/ip
丑八怪大丑3 小时前
Java网络编程
linux·服务器·网络
多看多敲多思考3 小时前
华润微CS32ME10 MCU使用教程(2)---CS32ME10之UART串口模块使用
stm32·单片机·嵌入式硬件·mcu
神一样的老师3 小时前
【兆易创新GD32VW553开发板试用】天气时钟设计与调试实战
单片机·嵌入式硬件·物联网
国科安芯4 小时前
核电站仪控与监测系统中抗辐射 MCU 芯片应用研究
单片机·嵌入式硬件·macos·无人机·cocos2d·核电站
想成为优秀工程师的爸爸4 小时前
第三十篇技术笔记:郭大侠学UDS - 人有生老三千疾,望闻问切良方医
网络·笔记·网络协议·tcp/ip·信息与通信
黑白园4 小时前
STM32系统时钟由72M修改为36M验证示例
stm32·单片机·嵌入式硬件
数智工坊4 小时前
【SAM-DETR论文阅读】:基于语义对齐匹配的DETR极速收敛检测框架
网络·论文阅读·人工智能·深度学习·transformer