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

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

为什么要用责任链模式?

在传统的串口协议处理中,我们常常会写一个巨大的 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校验、日志记录、解密)也可以做成独立的处理者,方便地在不同的责任链中复用。

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

相关推荐
聊点儿技术2 小时前
IPv6来了,IP精准定位服务还能“准”吗?
大数据·网络·人工智能·ip·ipv4·ipv6·ip精准定位
zandy10112 小时前
打破API瓶颈!衡石HENGSHI CLI:专为AI Agent打造,重构BI自动化底层逻辑
人工智能·重构·自动化
不吃鱼的猫7482 小时前
【音视频流媒体进阶:从网络到 WebRTC】第02篇-I/O 多路复用:从 select 到 epoll
网络·音视频·webrtc
时空自由民.2 小时前
LCD显示的图像散乱原因
单片机
CS创新实验室2 小时前
《计算机网络》深入学:比较 RIP 和 OSPF 协议
网络·计算机网络·智能路由器
merlin-mm2 小时前
GPU通信速率对比
网络·kubernetes
wanhengidc2 小时前
服务器管理器的作用有哪些?
运维·服务器·网络·安全·游戏·智能手机
vortex52 小时前
基于资源约束的委派 (RBCD) 利用细节
网络·网络协议·网络安全·内网渗透·ad域
cyber_两只龙宝2 小时前
【Docker】Docker的自定义网络详解
linux·运维·网络·docker·云原生·容器