视频在线制作网站做职业装的网站
2026/2/5 2:44:47 网站建设 项目流程
视频在线制作网站,做职业装的网站,wordpress图片自动下载,网络会议系统app专栏导读#xff1a;当一个请求可能被多个对象中的某一个处理#xff0c;但具体由谁处理在运行时才确定时#xff0c;责任链模式是最佳选择。在嵌入式通信中#xff0c;它能将复杂的混合协议解析解耦成一个个独立的“处理器”#xff0c;让你的代码像工厂流水线一样井井有…专栏导读当一个请求可能被多个对象中的某一个处理但具体由谁处理在运行时才确定时责任链模式是最佳选择。在嵌入式通信中它能将复杂的混合协议解析解耦成一个个独立的“处理器”让你的代码像工厂流水线一样井井有条。1. 场景还原 (The Pain)你负责维护一个DTU (数据传输单元)项目。串口数据进来后你需要判断它是什么格式。菜鸟的写法逻辑面条 (Spaghetti Code)// 这种函数在工业界随处可见往往长达 2000 行void UART_Parse_Super_Function(uint8_t* data, uint16_t len) {// 1. 先猜是不是 Modbus (判断包头和 CRC)if (data[0] 0x01 CheckCRC(data)) {Modbus_Process(data);}// 2. 猜是不是 AT 指令 (找 \r\nOK)else if (strstr((char*)data, OK\r\n)) {AT_Process(data);}// 3. 猜是不是调试命令else if (strncmp((char*)data, reboot, 6) 0) {System_Reset();}// ... 后面还有 GPS 协议、OTA 协议 ...else {// 甚至还有不知名的私有协议夹杂其中}}架构师的审视违反单一职责原则 (SRP)这个函数管得太宽了。只要任何一个协议的解析逻辑变动都要修改这个主函数。维护噩梦新加入一个成员负责开发“JSON 协议”他不得不小心翼翼地修改这个大函数稍有不慎就把同事写的 Modbus 解析搞挂了。优先级混乱if-else的顺序决定了优先级。如果想调整优先级比如让调试命令最先响应必须手动剪切粘贴代码块。2. 模式图解 (The Concept)责任链模式把每个协议解析器看作一个Handler (节点)。我们将这些节点串成一条链表。- (Next?) - [AT Handler] - (Next?) - [CLI Handler] - Drop]Request: 刚收到的原始数据包。Handler: 包含两个核心动作CanHandle(data): 我能处理这个数据吗Process(data): 处理数据。Next: 指向下一个接盘侠的指针。Flow: 数据从链头进入谁认识谁处理都不认识丢弃。3. 代码实战 (The Code)为了避免递归导致的栈溢出嵌入式大忌我们将采用迭代 (Iteration)方式实现链表遍历。3.1 定义抽象处理者 (Handler Interface)#include stdint.h#include stdbool.h#include stddef.h// 定义 Handler 结构体typedef struct ProtocolHandler_t ProtocolHandler;struct ProtocolHandler_t {const char* handler_name; // 用于调试// 核心判定函数返回 true 表示是我的菜false 表示不认识bool (*match)(ProtocolHandler* self, const uint8_t* data, uint16_t len);// 核心处理函数void (*process)(ProtocolHandler* self, const uint8_t* data, uint16_t len);// 链表指针ProtocolHandler* next;};3.2 实现具体处理者 (Concrete Handlers)Handler 1: Modbus 解析器// ModbusHandler.c#include Chain.hstatic bool IsModbus(ProtocolHandler* self, const uint8_t* data, uint16_t len) {// 简单的特征判断地址码 功能码合法性if (len 4) return false;if (data[0] ! 0x01) return false; // 假设本机地址 01return true;}static void HandleModbus(ProtocolHandler* self, const uint8_t* data, uint16_t len) {printf([%s] Processing Modbus Frame...\n, self-handler_name);// ... 具体业务逻辑 ...}// 静态定义对象无需 mallocstatic ProtocolHandler s_modbus_handler {.handler_name Modbus,.match IsModbus,.process HandleModbus,.next NULL};ProtocolHandler* Get_Modbus_Handler(void) {return s_modbus_handler;}Handler 2: AT 指令解析器// ATHandler.cstatic bool IsAT(ProtocolHandler* self, const uint8_t* data, uint16_t len) {// 判断是否包含 AT 或 OK// 注意实际项目中不要在中断里用 strstr这里仅作演示if (len 100) return false;return (data[0] A data[1] T);}static void HandleAT(ProtocolHandler* self, const uint8_t* data, uint16_t len) {printf([%s] Response AT Command...\n, self-handler_name);}static ProtocolHandler s_at_handler {.handler_name AT_Cmd,.match IsAT,.process HandleAT,.next NULL};ProtocolHandler* Get_AT_Handler(void) {return s_at_handler;}3.3 链条组装与运行 (Client)这是一个简单的链表管理器。// ProtocolManager.cstatic ProtocolHandler* s_head NULL;// 注册函数将 Handler 加入链表尾部 (或者头部视优先级而定)void Chain_Register(ProtocolHandler* node) {if (s_head NULL) {s_head node;} else {ProtocolHandler* cur s_head;while (cur-next ! NULL) {cur cur-next;}cur-next node;}}// 核心输入入口void Chain_Input(const uint8_t* data, uint16_t len) {ProtocolHandler* cur s_head;while (cur ! NULL) {// 1. 询问当前节点是否能处理if (cur-match(cur, data, len)) {// 2. 能处理 - 执行并结束cur-process(cur, data, len);return;}// 3. 不能处理 - 传给下一个cur cur-next;}// 4. 谁都没处理 - 丢弃或记录日志printf([Chain] Unknown Protocol, Drop!\n);}3.4 初始化流程// main.cvoid System_Init() {// 像搭积木一样组装协议栈// 优先级由注册顺序决定Chain_Register(Get_Modbus_Handler()); // 优先判断 ModbusChain_Register(Get_AT_Handler()); // 其次 ATChain_Register(Get_CLI_Handler()); // 最后 CLI}// 串口中断回调void UART_RxCpltCallback(uint8_t* buf, uint16_t len) {Chain_Input(buf, len);}4. 内存与性能分析 (The Cost)栈空间 (Stack Usage)优势传统的递归调用链A 调用 BB 调用 C会消耗大量栈空间。我们使用的while循环迭代法无论链条多长栈占用都是O(1)。这对 RAM 只有几 KB 的单片机至关重要。执行效率 (Latency)最坏情况如果链条上有 10 个协议而收到的包是第 10 个协议负责或者干脆是乱码那么 CPU 需要执行 10 次match函数。优化策略贪心排序。统计现网数据将出现频率最高的协议如 Modbus放在链表头部注册。这样 90% 的请求都在第一个节点被拦截效率极高。5. 变种与延伸 (The Evolution)5.1 动态插件系统 (Plugins)基于此模式你可以实现一个支持动态加载的系统。 比如你的设备支持 SD 卡加载驱动。你可以在运行时读取 SD 卡里的 Shared Object (若跑 Linux) 或特定的二进制代码将其中的 Handler 挂载到链表上。这意味着升级新协议不需要重启设备。5.2 责任链 观察者 (Chain Observer)目前的实现是“独占式”的一旦 Match 成功就 return。 有些场景需要“旁路监听”。例如你希望 Modbus 处理数据的同时流量统计模块也能看到这个数据包。改法在process后不直接return而是继续cur cur-next或者引入一个is_final标志位由 Handler 决定是否终结链条。5.3 复杂协议过滤器不仅仅是区分协议还可以用于数据预处理。Node 1:解密 Handler(如果不加密直接 pass加密则解密后修改 data 内容传给 Node 2)。Node 2:解压 Handler。Node 3:业务 Handler。 这样就构成了一个强大的数据处理管道 (Pipeline)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询