OpenHarmony解读之设备认证:数据接收管理-消息解析_openharmony软总线接收消息-程序员宅基地

技术标签: OpenHarmony源码分析  网络  安全  网络协议  tcp/ip  harmonyos  

一、概述

从本文开始,将介绍在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;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_46669761/article/details/125023391

智能推荐

获取大于等于一个整数的最小2次幂算法(HashMap#tableSizeFor)_整数 最小的2的几次方-程序员宅基地

文章浏览阅读2w次,点赞51次,收藏33次。一、需求给定一个整数,返回大于等于该整数的最小2次幂(2的乘方)。例: 输入 输出 -1 1 1 1 3 4 9 16 15 16二、分析当遇到这个需求的时候,我们可能会很容易想到一个"笨"办法:..._整数 最小的2的几次方

Linux 中 ss 命令的使用实例_ss@,,x,, 0-程序员宅基地

文章浏览阅读865次。选项,以防止命令将 IP 地址解析为主机名。如果只想在命令的输出中显示 unix套接字 连接,可以使用。不带任何选项,用来显示已建立连接的所有套接字的列表。如果只想在命令的输出中显示 tcp 连接,可以使用。如果只想在命令的输出中显示 udp 连接,可以使用。如果不想将ip地址解析为主机名称,可以使用。如果要取消命令输出中的标题行,可以使用。如果只想显示被侦听的套接字,可以使用。如果只想显示ipv4侦听的,可以使用。如果只想显示ipv6侦听的,可以使用。_ss@,,x,, 0

conda activate qiuqiu出现不存在activate_commandnotfounderror: 'activate-程序员宅基地

文章浏览阅读568次。CommandNotFoundError: 'activate'_commandnotfounderror: 'activate

Kafka 实战 - Windows10安装Kafka_win10安装部署kafka-程序员宅基地

文章浏览阅读426次,点赞10次,收藏19次。完成以上步骤后,您已在 Windows 10 上成功安装并验证了 Apache Kafka。在生产环境中,通常会将 Kafka 与外部 ZooKeeper 集群配合使用,并考虑配置安全、监控、持久化存储等高级特性。在生产者窗口中输入一些文本消息,然后按 Enter 发送。ZooKeeper 会在新窗口中运行。在另一个命令提示符窗口中,同样切换到 Kafka 的。Kafka 服务器将在新窗口中运行。在新的命令提示符窗口中,切换到 Kafka 的。,应显示已安装的 Java 版本信息。_win10安装部署kafka

【愚公系列】2023年12月 WEBGL专题-缓冲区对象_js 缓冲数据 new float32array-程序员宅基地

文章浏览阅读1.4w次。缓冲区对象(Buffer Object)是在OpenGL中用于存储和管理数据的一种机制。缓冲区对象可以存储各种类型的数据,例如顶点、纹理坐标、颜色等。在渲染过程中,缓冲区对象中存储的数据可以被复制到渲染管线的不同阶段中,例如顶点着色器、几何着色器和片段着色器等,以完成渲染操作。相比传统的CPU访问内存,缓冲区对象的数据存储和管理更加高效,能够提高OpenGL应用的性能表现。_js 缓冲数据 new float32array

四、数学建模之图与网络模型_图论与网络优化数学建模-程序员宅基地

文章浏览阅读912次。(1)图(Graph):图是数学和计算机科学中的一个抽象概念,它由一组节点(顶点)和连接这些节点的边组成。图可以是有向的(有方向的,边有箭头表示方向)或无向的(没有方向的,边没有箭头表示方向)。图用于表示各种关系,如社交网络、电路、地图、组织结构等。(2)网络(Network):网络是一个更广泛的概念,可以包括各种不同类型的连接元素,不仅仅是图中的节点和边。网络可以包括节点、边、连接线、路由器、服务器、通信协议等多种组成部分。网络的概念在各个领域都有应用,包括计算机网络、社交网络、电力网络、交通网络等。_图论与网络优化数学建模

随便推点

android 加载布局状态封装_adnroid加载数据转圈封装全屏转圈封装-程序员宅基地

文章浏览阅读1.5k次。我们经常会碰见 正在加载中,加载出错, “暂无商品”等一系列的相似的布局,因为我们有很多请求网络数据的页面,我们不可能每一个页面都写几个“正在加载中”等布局吧,这时候将这些状态的布局封装在一起就很有必要了。我们可以将这些封装为一个自定布局,然后每次操作该自定义类的方法就行了。 首先一般来说,从服务器拉去数据之前都是“正在加载”页面, 加载成功之后“正在加载”页面消失,展示数据;如果加载失败,就展示_adnroid加载数据转圈封装全屏转圈封装

阿里云服务器(Alibaba Cloud Linux 3)安装部署Mysql8-程序员宅基地

文章浏览阅读1.6k次,点赞23次,收藏29次。PS: 如果执行sudo grep 'temporary password' /var/log/mysqld.log 后没有报错,也没有任何结果显示,说明默认密码为空,可以直接进行下一步(后面设置密码时直接填写新密码就行)。3.(可选)当操作系统为Alibaba Cloud Linux 3时,执行如下命令,安装MySQL所需的库文件。下面示例中,将创建新的MySQL账号,用于远程访问MySQL。2.依次运行以下命令,创建远程登录MySQL的账号,并允许远程主机使用该账号访问MySQL。_alibaba cloud linux 3

excel离散度图表怎么算_excel离散数据表格-Excel 离散程度分析图表如何做-程序员宅基地

文章浏览阅读7.8k次。EXCEL中数据如何做离散性分析纠错。离散不是均值抄AVEDEV……=AVEDEV(A1:A100)算出来的是A1:A100的平均数。离散是指各项目间指标袭的离散均值(各数值的波动情况),数值较低表明项目间各指标波动幅百度小,数值高表明波动幅度较大。可以用excel中的离散公式为STDEV.P(即各指标平均离散)算出最终度离散度。excel表格函数求一组离散型数据,例如,几组C25的...用exc..._excel数据分析离散

学生时期学习资源同步-JavaSE理论知识-程序员宅基地

文章浏览阅读406次,点赞7次,收藏8次。i < 5){ //第3行。int count;System.out.println ("危险!System.out.println(”真”);System.out.println(”假”);System.out.print(“姓名:”);System.out.println("无匹配");System.out.println ("安全");

linux 性能测试磁盘状态监测:iostat监控学习,包含/proc/diskstats、/proc/stat简单了解-程序员宅基地

文章浏览阅读3.6k次。背景测试到性能、压力时,经常需要查看磁盘、网络、内存、cpu的性能值这里简单介绍下各个指标的含义一般磁盘比较关注的就是磁盘的iops,读写速度以及%util(看磁盘是否忙碌)CPU一般比较关注,idle 空闲,有时候也查看wait (如果wait特别大往往是io这边已经达到了瓶颈)iostatiostat uses the files below to create ..._/proc/diskstat

glReadPixels读取保存图片全黑_glreadpixels 全黑-程序员宅基地

文章浏览阅读2.4k次。问题:在Android上使用 glReadPixel 读取当前渲染数据,在若干机型(华为P9以及魅族某魅蓝手机)上读取数据失败,glGetError()没有抓到错误,但是获取到的数据有误,如果将获取到的数据保存成为图片,得到的图片为黑色。解决方法:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓..._glreadpixels 全黑