自定义协议的命令解析器

本文探讨了当前命令解析方式存在的问题,如switch...case的弊端,提出了一种利用结构体和函数指针实现命令解析器的改进方案,以实现命令处理的高内聚低耦合。通过命令注册、查找匹配和执行指令,避免跨模块修改,并讨论了如何处理参数传递和架构影响。最后,作者分享了将这种解析器应用于实际项目后的体验和新认识。
摘要由CSDN通过智能技术生成

前言

  对于目前公司的命令/报文解析方式不爽有段时间了,因为一个函数竟然写了百行左右,那些switch…case 的case 直接用数字表示,而且那个数字真是毫无顺序可言(至少我是找不到),每次找命令都是从头看到尾,一遍没看到,再看一遍,还是没有,才能确认确实没有相应的指令。对于修改和维护来说,我一直有种自己在“添乱”的感觉。但是更不爽的是我没能力解决这个问题,直到看到了那篇文章:【命令解析器 :C语言对象化设计实例】

  如果没有面向对象编程经验的话那篇文章可以先跳过,看看我的描述能不能解决实际问题,如果不能的话,起码会产生种意识:这个问题很小,看一篇文章就能解决,但博主语言组织能力太差,害我不得不多看一篇文章。

  自定义协议的命令解析器: 是在接收到自定义协议的报文的情况下用于对命令进行解析。本质上就是发生相应的事件,执行相应功能。

Switch…case类型命令解析的弊端

  那篇文章提出了switch…case 那种方式更客观清晰的弊端描述,像“添乱”这个词太主观了,大概只有我自己才知道这个具体意味着什么。

  • 大量的case分支对程序的复杂度时明显增加的,非常不便于查找,排错和维护。

  • 命令增加引起跨模块修改。只是增加了GPIO相关功能,命令处理逻辑没变(依然只是判断字符串相等),为什么却要改动cmd.c(命令解析源文件)的命令处理逻辑。

  • 大量的外部函数,模块间高度耦合。在cmd.c (命令解析源文件)中直接通过函数名调用,两个文件(命令解析文件 和 被调用函数所在文件)像缠绵的情侣般高度耦合,这种紧密的联系破坏了C程序设计的一个基本原则—模块的独立性。采用 了模块化编程,然而每个模块却不能独立使用,这是会破坏模块化的,若不加维护和避免,可能会拖垮优良的架构。

程序的改进目标

  • 命令的处理函数要去耦合,高内聚低耦合的的代码是易于复用和维护的
  • 增加或减少命令不影响cmd.c(命令解析源文件)

对命令解析器的分析

【命令】本身可以封装为 包含 【命令名】和【对应操作】两个成员的结构体

  • 【命令名】:属性,可以用字符数组存储

  • 【对应操作】:行为/函数,由于C语言结构体不支持函数,可用函数指针存储。

【命令解析器】的处理过程:命令注册,查找匹配 和 执行指令

通过调用【命令解析器】主动注册命令,而不是通过代码写死,从而**避免了跨模块修改**

【命令解析器】需要从一对命令中匹配一个,因此需要一种能存储命令集合的数据结构:线性表(列表内容;列表长度)。线性表的基本操作与命令处理过程结合(命令的注册和匹配 其实就是 插入和查找过程)

命令解析器的代码示例

【数据结构】:结构体数组(线性表)+ 存储命令个数的变量

typedef void (*handler)(void);        // 命令操作函数指针类型
 
/* 命令结构体类型 */
typedef struct cmd
{
   
    char cmd_name[MAX_CMD_NAME_LENGTH + 1];   // 命令名 
    handler cmd_operate;                      // 命令操作函数
} CMD;

// 文件名称:cmd.h
 
/* 命令列表结构体类型 */
typedef struct cmds
{
   
    CMD cmds[MAX_CMDS_COUNT];  // 列表内容
    int num;                   // 列表长度
} CMDS;

【注册命令函数】:接收一个命令类型数组,插入到命令解析器的命令列表中。

// 文件名称:cmd.c
 
void register_cmds(CMD reg_cmds[], int length)
{
   
    int i;
 
    if (length > MAX_CMDS_COUNT)
    {
   
        return;
    }
 
    for (i = 0; i < length; i++)
    {
   
        if (commands.num < MAX_CMDS_COUNT)  // 命令列表未满
        {
   
            strcpy(commands.cmds[commands.num].cmd_name, reg_cmds[i].cmd_name);
            commands.cmds[commands.num].cmd_operate = reg_cmds[i].cmd_operate;
            commands.num++;
        }  
    }  
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
包括源程序和详细的使用说明个,同时内付相关的软件 编程环境: 操作系统:Windows XP SP2 开发语言:C++ / MFC 编译环境:MS Visual C++6.0 第3方函数库:Winpcap3.1 使用说明: 1. 启动: 程序启动后首先出现网卡选择对话框,如图1所示,在设备列表中选择需要进行捕获的网卡。 2. 界面: 选择网卡后出现程序主界面,界面设计主要参照了一款比较优秀的网络协议分析工具Iris的图形化界面,采用切分窗口风格,整个程序界面分为三部分:左侧为树形视图,右侧上半部分为列表视图,右侧下半部分为编辑视图 3. 功能: 1) 如果不做任何设置,程序默认为混杂模式,点击按钮① 后,程序开始捕获网络上传输的所有数据包,并将它们显示在列表视图中。点击按钮② 停止捕获。 2) 点击列表视图中的某一行后,对应于该数据包的详细字段分析和原始数据将分别显示在左侧树形视图和右下的编辑视图中,如图2所示。 3) 列表视图中显示的最大数据包数量为2000条,超过后列表视图将清空并重新开始。另外程序还提供了在任意时刻清空当前列表视图中所有数据包的命令(提供该命令的原因是经常会有这样的情况:即列表视图中已经有了很多消息了,但目前只需要关注从即刻起之后到来的消息,这时以前的消息就不再有用并且会影响我们对所要消息的选择)。要使用该命令请在列表视图中任意位置单击鼠标右键,在弹出的菜单中选择“全部清空”
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值