【解决了一个json解析segment fault的问题】

解决了一个json解析segment fault的问题

问题

最近在做第二代边缘采集网关,我把第一代网关的软件移植过来。测试发现了个问题,数据中转功能下配置界面异常。我定位发现是之前版本网关的代码报segment fault导致,写这段代码的同事已离职,我来修好它。需要解析的json文件如下:

[
  {
    "Gw_vid": 336597760,
    "IP": "192.168.101.66",
    "Conn_count": 2,
    "Name": " 336597760",
    "Gw_conn": [
      {
        "Blk_num": 1,
        "Data_id": 1659602160,
        "Dev_id": 1,
        "Port": 80,
        "Blk_name": "1",
        "Reg_blk": [
          {
            "Start_addr": 0,
            "Len": 60,
            "Data_filed_name": "area",
            "Read_cmd": 3,
            "Write_cmd": 10
          }
        ]
      },
      {
        "Blk_num": 1,
        "Data_id": 1659602624,
        "Dev_id": 200,
        "Port": 80,
        "Blk_name": "200",
        "Reg_blk": [
          {
            "Start_addr": 0,
            "Len": 60,
            "Data_filed_name": "area",
            "Read_cmd": 3,
            "Write_cmd": 10
          }
        ]
      }
    ]
  }
]

是用c语言来解析的,头文件部分代码如下:

typedef struct reg_blk_s
{
    unsigned short start_addr;
    unsigned short reg_len;
    unsigned char read_cmd;
    unsigned char write_cmd;
    unsigned char *val;
}reg_blk_s;

typedef struct oth_conn_s
{
    unsigned int data_id;
    unsigned int port;
    void *ctx;
    unsigned char dev_id;
    unsigned char online;
    unsigned int reg_blk_num;
    reg_blk_s *reg_blk; 
}oth_conn_s;


typedef struct oth_gw_s
{
    unsigned int gw_vid;
    char IP[16];
    bool dump_switch;
    unsigned int online_count;
    unsigned int gw_conn_count;
    oth_conn_s *gw_conn;
}oth_gw_data_s;

程序引入了cJSON库来解析json格式数据,解析出来后保存在全局变量里,这个全局变量直接是一个比较大的数组,有点偷懒,实现代码如下:

#include "cJSON.h"

oth_gw_data_s g_trans_conf[128];

int slave_get_configure(cJSON * root)
{
	int i =0;
	cJSON * item = NULL;
    static int  gw_count = 0,conn_count = 0,blk_count = 0;

    for(i=0; i<cJSON_GetArraySize(root); i++)
    {
        item = cJSON_GetArrayItem(root, i);       
		switch(item->type){
			case cJSON_Object:
                slave_get_configure(item);
                break;
            case cJSON_Array:
				if(NULL != item->string ){
                    if(0 == strcmp("Reg_blk",item->string))
                    {
                        slave_get_configure(item);
                    }else if(0 == strcmp("Gw_conn",item->string))
                    {
                        conn_count = 0;
                        blk_count = 0;
                        slave_get_configure(item);
                    }
				} 
				break;
			case cJSON_Number:
				if(0 == strcmp(item->string,"Gw_vid")){
                    g_trans_conf[gw_count].gw_vid = item->valueint;
                }else if(0 == strcmp(item->string,"Data_id")){
                    g_trans_conf[gw_count].gw_conn[conn_count].data_id = item->valueint;
                }else if(0 == strcmp(item->string,"Port")){
                    g_trans_conf[gw_count].gw_conn[conn_count].port=item->valueint;
                }else if(0 == strcmp(item->string,"Dev_id")){
                    g_trans_conf[gw_count].gw_conn[conn_count].dev_id=item->valueint;
                }else if(0 == strcmp(item->string,"Blk_num")){
                    g_trans_conf[gw_count].gw_conn[conn_count].online = METER_STATE_OFFLINE;
                    g_trans_conf[gw_count].gw_conn[conn_count].ctx = NULL;
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk_num=item->valueint;
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk = \
                        malloc(item->valueint * sizeof(reg_blk_s));
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].read_cmd = 0x03;
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].write_cmd = 0x10;
                }else if(0 == strcmp(item->string,"Start_addr")){
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].start_addr = item->valueint;
                }else if(0 == strcmp(item->string,"Read_cmd")){
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].read_cmd = item->valueint;
                }else if(0 == strcmp(item->string,"Write_cmd")){
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].write_cmd = item->valueint;
                }else if(0 == strcmp(item->string,"Len")){
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].reg_len =item->valueint;
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].val = malloc(item->valueint * 2);
                    blk_count++;
                    if(blk_count == g_trans_conf[gw_count].gw_conn[conn_count].reg_blk_num)
                    {
                        conn_count++;
                        blk_count = 0;
                        if(conn_count == g_trans_conf[gw_count].gw_conn_count)
                        {
                            conn_count = 0;
                            gw_count++;
                        }
                    }
                }else if(0 == strcmp(item->string,"Conn_count"))
                {
                    g_trans_conf[gw_count].dump_switch = true;
                    g_trans_conf[gw_count].online_count = 0;
                    g_trans_conf[gw_count].gw_conn_count = item->valueint;
                    g_trans_conf[gw_count].gw_conn = malloc(item->valueint * sizeof(oth_conn_s));
                }
				break;
			case cJSON_String:
                if(0 == strcmp(item->string,"IP")){
                    strcpy(g_trans_conf[gw_count].IP,item->valuestring);
                }
				break;

			default:
 				break;	
		}
    }
    return gw_count;
}

我看了上面代码的逻辑,应该是遇到合适的数据就缓存,如果是对象就递归,这样代码倒是比较紧凑,风险在于不是逐层解析的,容易有隐患。上面代码解析到"Len"这个key时(54-66行间),会执行"conn_count++",原来json文件"Len"是最后一个key,没有问题。后来又加了几个key,就出事了。

"Reg_blk": [
          {
            "Start_addr": 0,
            "Len": 60,
            "Data_filed_name": "area",
            "Read_cmd": 3,
            "Write_cmd": 10
          }

"conn_count"改变后,全局变量的下标指到了下一个位置,此为null,所以解析"Read_cmd"时就报segment fault了

}else if(0 == strcmp(item->string,"Read_cmd")){
                    g_trans_conf[gw_count].gw_conn[conn_count].reg_blk[blk_count].read_cmd = item->valueint;
}

注意,代码里面没有解析"Data_filed_name"所以是在"Read_cmd"那里报的错。原因很清楚了,想要快点修复这个bug只要改变"conn_count++"的位置就好了,但是我觉得代码有隐患,所以花时间一并改了。

解决方案

新的解析代码如下,长了很多。。。

#include "cJSON.h"

oth_gw_data_s *g_trans_conf;
unsigned char g_gw_count;

/**
* @brief		释放 g_trans_conf 
* @param
* @retval		
* @author		zhongwei.peng
* @date		    2022-8-10
*/
void slave_deinit_confs(void)
{
    int i,j,k;

    if (g_trans_conf == NULL) 
        return;

    for (i = 0; i < g_gw_count; i++) 
    {
        if (g_trans_conf[i].gw_conn != NULL) 
        {
            for (j = 0; j < g_trans_conf[i].gw_conn_count; j++) 
            {
                if (g_trans_conf[i].gw_conn[j].reg_blk != NULL) 
                {
                    for (k = 0; k < g_trans_conf[i].gw_conn[j].reg_blk_num; k++) 
                    {
                        if (g_trans_conf[i].gw_conn[j].reg_blk[k].val != NULL)
                            free(g_trans_conf[i].gw_conn[j].reg_blk[k].val);
                    }

                    free(g_trans_conf[i].gw_conn[j].reg_blk);
                }
            }

            free(g_trans_conf[i].gw_conn);
        }
    }

    free(g_trans_conf);
    g_trans_conf = NULL;
    g_gw_count = 0;

    return;
}

/**
* @brief		获取 reg_blk_s
* @param[out]   reg_blk, 寄存器数据区缓存,解析出来的数据缓存到这里
* @param[in]    root, 要解析的json对象
* @retval		成功返回0,否则返回<0
* @author		zhongwei.peng
* @date		    2022-8-10
*/
int slave_get_reg_blk(reg_blk_s *reg_blk, cJSON *root)
{
    cJSON *object_item = NULL;

    object_item = cJSON_GetObjectItem(root, "Start_addr");
    if (object_item != NULL) 
        reg_blk->start_addr = object_item->valueint;
    else
        return -1;

    object_item = cJSON_GetObjectItem(root, "Len");
    if (object_item != NULL) 
    {
        reg_blk->reg_len = object_item->valueint;
        reg_blk->val = malloc(reg_blk->reg_len * 2);
        if (reg_blk->val == NULL)
            return -1;
    }
    else
    {
        return -1;
    }

    object_item = cJSON_GetObjectItem(root, "Read_cmd");
    if (object_item != NULL) 
        reg_blk->read_cmd = object_item->valueint;
    else
        return -1;

    object_item = cJSON_GetObjectItem(root, "Write_cmd");
    if (object_item != NULL) 
        reg_blk->write_cmd = object_item->valueint;
    else
        return -1;

    return 0;
}

/**
* @brief		获取 oth_conn_s
* @param[out]   gw_conn, 连接对象首指针,解析出来的数据缓存到这里
* @param[in]    root, 要解析的json对象
* @retval		成功返回0,否则返回<0
* @author		zhongwei.peng
* @date		    2022-8-10
*/
int slave_get_gw_conn(oth_conn_s *gw_conn, cJSON *root)
{
    int i;
    int ret;
    cJSON *object_item = NULL;

    object_item = cJSON_GetObjectItem(root, "Data_id");
    if (object_item != NULL) 
        gw_conn->data_id = object_item->valueint;
    else
        return -1;

    object_item = cJSON_GetObjectItem(root, "Dev_id");
    if (object_item != NULL) 
        gw_conn->dev_id = object_item->valueint;
    else
        return -1;

    object_item = cJSON_GetObjectItem(root, "Port");
    if (object_item != NULL) 
        gw_conn->port = object_item->valueint;
    else
        return -1;

    object_item = cJSON_GetObjectItem(root, "Reg_blk");
    if (object_item != NULL) 
    {
        gw_conn->reg_blk_num = cJSON_GetArraySize(object_item);
        if (gw_conn->reg_blk_num > 0)
        {
            gw_conn->reg_blk = (reg_blk_s *)malloc(gw_conn->reg_blk_num * sizeof(reg_blk_s));
            if (gw_conn->reg_blk != NULL)
            {
                ret = 0;
                for (i = 0; i < gw_conn->reg_blk_num; i++) 
                    ret |= slave_get_reg_blk(&(gw_conn->reg_blk[i]), cJSON_GetArrayItem(object_item, i));

                if (ret == 0) 
                    return 0;
            }
        }
    }

    return -1;
}

/**
* @brief		获取 oth_gw_data_s
* @param[out]   p_gw_data, 网关数据区对象首指针,解析出来的数据缓存到这里
* @param[in]    root, 要解析的json对象
* @retval		成功返回0,否则返回<0
* @author		zhongwei.peng
* @date		    2022-8-10
*/
int slave_get_gw_data(oth_gw_data_s *p_gw_data, cJSON *root)
{
    cJSON *object_item = NULL;
    int i;
    int ret;

    object_item = cJSON_GetObjectItem(root, "Gw_vid");
    if (object_item != NULL) 
        p_gw_data->gw_vid = object_item->valueint;
    else
        return -1;

    object_item = cJSON_GetObjectItem(root, "IP");
    if (object_item != NULL) 
        strcpy(p_gw_data->IP, object_item->valuestring);
    else
        return -1;

    object_item = cJSON_GetObjectItem(root, "Gw_conn");
    if (object_item != NULL) 
    {
        p_gw_data->gw_conn_count = cJSON_GetArraySize(object_item);
        if (p_gw_data->gw_conn_count > 0) 
        {
            p_gw_data->gw_conn = (oth_conn_s *)malloc(p_gw_data->gw_conn_count * sizeof(oth_conn_s));
            if (p_gw_data->gw_conn != NULL) 
            {
                ret = 0;
                for (i = 0; i < p_gw_data->gw_conn_count; i++) 
                    ret |= slave_get_gw_conn(&(p_gw_data->gw_conn[i]), cJSON_GetArrayItem(object_item, i));

                if (ret == 0) 
                    return 0;
            }
            
        }
    }

    return -1;
}

/**
* @brief		解析配置文件,数据保存到全局变量 g_trans_conf
* @param[in]    root, 要解析的json对象
* @retval		返回网关个数
* @author		zhongwei.peng
* @date		    2022-8-10
*/
unsigned char slave_get_configure(cJSON *root)
{
    int i, count;
    int ret;

    count = cJSON_GetArraySize(root);
    if (count > 0) 
    {
        g_trans_conf = (oth_gw_data_s *)malloc(count * sizeof(oth_gw_data_s));
        if (g_trans_conf != NULL) 
        {
            memset(g_trans_conf, 0, (count * sizeof(oth_gw_data_s)));
            g_gw_count = count;
            ret = 0;
            for (i = 0; i < count; i++) 
                ret |= slave_get_gw_data(&(g_trans_conf[i]), cJSON_GetArrayItem(root, i));

            if (ret == 0) 
                return count;
        }
    }

    slave_deinit_confs();
    return 0;
}

修改的地方有3个,一是全局变量原来是固定128个的数组,改为动态申请。二是分层解析json配置,一个json对象对应一个数据结构对应一个解析函数。三是添加空间释放代码。

总结

json的解析使用广泛,后面应该把c语言相关JSON库源码读读,加深了解。另外,自己觉得不够精简,最好参考一下别人怎么做。理想的是有个通用的API,直接把json对象解析到结构体去,例如:

int parse_json_object(cJSON *root, object_data_s *obj);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值