> 文档中心 > OpenHarmony解读之设备认证:数据接收管理-消息解析

OpenHarmony解读之设备认证:数据接收管理-消息解析


一、概述

从本文开始,将介绍在HiChain机制中如何处理本端接收到的数据及其响应对端的过程。这个过程的入口函数receive_data,在receive_data函数中,主要分为三个阶段:消息解析阶段消息处理阶段通知对端阶段,本文的重点是总体分析receive_data函数,然后对消息解析阶段的部分内容进行分析。

二、源码分析

这一模块的源码位于:/base/security/deviceauth。

  1. receive_data函数的整体分析:
/*函数功能:HiChain处理接收到的消息数据函数参数:    handle:HiChain实例对象,这里其实是void *类型    data:待处理的的消息数据函数返回值:    成功:返回0    失败:返回其他*/DLL_API_PUBLIC int32_t receive_data(hc_handle handle, struct uint8_buff *data){    LOGI("Begin receive data");    check_ptr_return_val(handle, HC_INPUT_ERROR);//检查实例对象是否为空    check_ptr_return_val(data, HC_INPUT_ERROR);//检查数据是否为空    check_ptr_return_val(data->val, HC_INPUT_ERROR);//检查数据地址是否为空    LOGI("Receive data from peer");    struct hichain *hichain = (struct hichain *)handle;//定义局部变量接收该hichain实例    struct message receive = { 0, 0, 0 };//初始化接收消息体    struct message send = { INFORM_MESSAGE, 0, 0 };//初始化发送消息体    void *send_data = NULL;//声明发送消息的地址    uint32_t send_data_len = 0;    int32_t ret = deserialize_message(data, &receive);//反序列化/无序化消息,即解析data原始消息内容,封装成格式化的消息保存在receive指向的地址空间中,通过执行对应消息码类型的解析函数进行解析    if (ret != HC_OK) { goto inform;    } struct header_analysis nav = navigate_message(receive.msg_code);//导航消息,根据消息码查表,得到 "消息模块-消息类型(消息码低四位)-是否请求消息" 一一对应的格式    ret = check_message_support(hichain, &nav, &receive);//检查消息是否可支持,解析出操作码,并与消息码进行对应,检查是否一致    if (ret != HC_OK) { goto inform;    }    //若消息合法且系统可支持,则继续    ret = build_object(hichain, nav.modular, !nav.is_request_msg, NULL);//构建HC子对象,根据nav.is_request_msg判断是否属于客户端    if (ret != HC_OK) { goto inform;    }    ret = proc_message(hichain, &nav, &receive, &send);//根据modular和is_request_msg查询全局分布式消息表,找到对应的消息处理函数并执行    if (ret != HC_OK) { goto inform;    }    ret = connect_message(hichain, &nav, &send);//连接消息inform://通知对端    encap_inform_message(ret, &send);//为"通知消息"申请内存空间    /* serialization 消息序列化,即构造通知消息*/    ret = build_send_data_by_struct(&send, &send_data, &send_data_len);//构造结构化的发送消息    if (ret == HC_OK) { DBG_OUT("Send data to peer"); hichain->cb.transmit(&hichain->identity, send_data, send_data_len);//调用软总线模块回调函数进行数据传输 FREE(send_data);    } else if (ret == HC_NO_MESSAGE_TO_SEND) { LOGI("Had no message to send"); ret = HC_OK;    } else { LOGE("build send data failed, error code is %d", ret);    }    set_result(hichain, receive.msg_code, send.msg_code, ret);//设置最终结果    destroy_receive_data_struct(&receive);//销毁接收数据结构    destroy_send_data(&send);//销毁发送数据空间    LOGI("End receive data");    return ret; /* hc_error */}
  1. receive_data函数中,首先调用deserialize_message函数对消息进行无序化处理,具体分析如下:
/*函数功能:反序列化/无序化消息函数参数:    data:接收到的数据地址,传入参数    receive:用于保存反序列化后消息的地址,传出参数函数返回值:    成功:返回0    失败:返回错误码详细:    主要进行消息码的解析和不同类型消息有效负载的解析,将解析后的消息数据保存在receive参数的地址空间中*/static int32_t deserialize_message(const struct uint8_buff *data, struct message *receive){    /* message head deserialization 消息头反序列化*/    struct pass_through_data *pass_through_data = parse_data((const char *)data->val);//从收到的数据中解析出消息码和有效负载,将有效载荷由cjson格式的数据转化为无格式的json字符串    if (pass_through_data == NULL) { LOGE("Parse data failed"); return HC_BUILD_OBJECT_FAILED;    }    /* message payload deserialization 有效负载反序列化,根据消息码为不同的消息类型提供对应的解析函数,然后执行对应解析函数进行解析,将解析完成后的消息保存到receive参数中*/    int32_t ret = build_struct_by_receive_data(pass_through_data->message_code, pass_through_data->payload_data,     JSON_STRING_DATA, receive);    if (ret != HC_OK) { LOGE("Build struct by receive data failed, error code is %d", ret);    }    free_data(pass_through_data);    pass_through_data = NULL;    return ret;}
  1. parse_data函数:从收到的数据中解析出消息码和有效负载,将有效载荷由cjson格式的数据转化为无格式的json字符串。
/*函数功能:解析收到的数据成固定的pass_through_data消息格式,解析出数据之中的消息码和有效负载函数参数:    data:数据地址函数返回值:    成功:返回pass_through_data格式化后的数据,包含消息码    失败:返回NULL详细:    解析出消息码和有效负载之后,将有效载荷由cjson格式的数据转化为无格式的字符串*/struct pass_through_data *parse_data(const char *data){    const char *payload = NULL;    struct pass_through_data *msg_data = (struct pass_through_data *)MALLOC(sizeof(struct pass_through_data));//为解析后的消息数据申请内存空间    if (msg_data == NULL) { return NULL;    }    json_handle obj = parse_json(data);//将json格式的数据解析成cjson结构体对象    if (obj == NULL) { LOGE("Passthrough Data parse_json failed"); goto error;    }    int32_t message_code = get_json_int(obj, FIELD_MESSAGE);//获取该cjson数据中的int类型的数据,即为消息码;子实体为FIELD_MESSAGE="message"    if (message_code == -1) { LOGE("Passthrough Data failed, field is null in message"); goto error;    }    json_pobject obj_value = get_json_obj(obj, FIELD_PAYLOAD);//获取该cjson对象中的FIELD_PAYLOAD="payload"子对象    payload = json_to_string(obj_value);//将Cjson结构体格式的对象转换为无格式字符串    if (payload == NULL) { LOGE("Passthrough Data failed, field is null in payload"); goto error;    }    (void)memset_s(msg_data, sizeof(*msg_data), 0, sizeof(*msg_data));//清空msg_data地址空间    msg_data->message_code = message_code;//赋值解析出来的消息码    int32_t len = strlen(payload);    if (len > 0) { ++len; /* add terminator 添加终止符*/ char *tmp_data = (char *)MALLOC(len);//申请暂存payload数据的内存空间 if (tmp_data == NULL) {     goto error; } (void)memset_s(tmp_data, len, 0, len);//清空tmp_data地址空间 (void)memcpy_s(tmp_data, len, payload, len);//将payload字符串拷贝到tmp_data中 msg_data->payload_data = tmp_data;//将该字符串数据直接赋给消息的有效载荷    }    FREE((char *)payload);//释放payload空间    free_json(obj);//释放Cjson对象obj    return msg_data;error:    if (payload != NULL) { FREE((char *)payload);    }    free_json(obj);//释放cjson结构体对象    FREE(msg_data);    return NULL;}//将json格式的数据解析成cjson结构体对象json_handle parse_json(const char *data){    cJSON *root = NULL;    if (data != NULL) { root = cJSON_Parse(data);//将json格式的数据解析成cjson结构体对象    }    return (void *)root;}//将json格式的数据解析成cjson结构体对象json_handle parse_json(const char *data){    cJSON *root = NULL;    if (data != NULL) { root = cJSON_Parse(data);//将json格式的数据解析成cjson结构体对象    }    return (void *)root;}//获取该cjson对象中的field子对象json_pobject get_json_obj(json_pobject parent, const char *field){    return cJSON_GetObjectItem((cJSON *)parent, field);//通过键名称在该root节点下查找子节点}//将Cjson结构体格式的对象转换为无格式字符串char *json_to_string(json_pobject obj){    if (obj != NULL) { //需要注意的是  json 格式的数据,虽然也是一个字符串的样子,但这个时候还是无法当成普通的字符串进行使用, //需要调用 cJSON_PrintUnformatted(root) 或者 cJSON_Print(root)来将json对象转换成普通的字符串,并且都是以该json对象的根为基点。 //两个API的区别即是:一个是没有格式的:也就是转换出的字符串中间不会有"\n" "\t"之类的东西存在,而cJSON_Print(root)打印出来是人看起来很舒服的格式. char *ret = cJSON_PrintUnformatted(obj); return ret;    }    return NULL;}
  1. build_struct_by_receive_data函数:根据消息码为不同的消息类型提供对应的解析函数,然后执行对应解析函数进行解析,将解析完成后的消息保存到receive参数中。
/*函数功能:根据消息码为不同的消息类型提供对应的解析函数,然后执行对应解析函数进行解析,将解析完成后的消息保存到message参数中函数参数:    msg_code:消息码    payload_data:有效负载地址    type:json对象的数据类型    message:传出参数,保存解析完成的消息数据函数返回值:    成功:返回0;    失败:返回错误码详细:*/static int32_t build_struct_by_receive_data(uint32_t msg_code, const char *payload_data,    enum json_object_data_type type, struct message *message){    const struct parse_message_map map[] = { { PAKE_REQUEST, parse_pake_request },//PAKE请求消息   { PAKE_RESPONSE, parse_pake_response },//PAKE响应消息   { PAKE_CLIENT_CONFIRM, parse_pake_client_confirm },//PAKE客户端认证消息   { PAKE_SERVER_CONFIRM_RESPONSE, parse_pake_server_confirm },//PAKE服务端认证响应消息   { AUTH_START_REQUEST, parse_auth_start_request },//认证开始请求消息   { AUTH_START_RESPONSE, parse_auth_start_response },//认证开始响应消息   { AUTH_ACK_REQUEST, parse_auth_ack_request },//认证确认请求消息   { AUTH_ACK_RESPONSE, parse_auth_ack_response },//认证确认响应消息   { ADD_AUTHINFO_REQUEST, parse_add_auth_info_request },//添加认证信息请求消息   { REMOVE_AUTHINFO_REQUEST, parse_rmv_auth_info_request },//移除认证信息请求信息   { ADD_AUTHINFO_RESPONSE, parse_add_auth_info_response },//添加认证信息响应消息   { REMOVE_AUTHINFO_RESPONSE, parse_rmv_auth_info_response },//移除认证信息响应消息   { EXCHANGE_REQUEST, parse_exchange_request },//交换请求   { EXCHANGE_RESPONSE, parse_exchange_response },//交换响应   { SEC_CLONE_START_REQUEST, sec_clone_parse_client_request },//安全克隆启动请求   { SEC_CLONE_ACK_REQUEST, sec_clone_parse_client_ack } };//安全克隆确认请求    for (uint32_t i = 0; i < sizeof(map) / sizeof(struct parse_message_map); i++) {//遍历消息解析表,查找对应消息码的解析函数,然后执行解析函数 if (map[i].msg_code != msg_code) {     continue; } void *payload = map[i].parse_message(payload_data, type);//执行对应消息码的解析函数!!! if (payload == NULL) {     return HC_BUILD_OBJECT_FAILED;//返回错误码 } message->msg_code = map[i].msg_code;//赋值消息码 message->payload = payload;//赋值解析后的消息有效负载 return HC_OK;    }    LOGE("Unsupport parse 0x%04x message", message->msg_code);//未知消息    return HC_UNKNOW_MESSAGE;}