OpenHarmony分布式调度详解|会话消息解析(2)

100 篇文章 1 订阅
100 篇文章 0 订阅

往期知识点记录:

前言

上一篇博客介绍了Service和Feature的定义和注册,Feature注册并初始化后,由软总线发布,并创建一个Session会话,用于处理TCP数据。

Session会话数据解析

代码分析

和Session会话有关的回调函数包含三个成员onBytesReceived、onSessionOpened、onSessionOpened,被封装为一个ISessionListener对象,定义如下:

/**
* onBytesReceived:处理接收到的byte数据
* onSessionOpened:打开Session会话
* onSessionClosed:关闭Session会话
*/
static struct ISessionListener g_sessionCallback = {
    .onBytesReceived = OnBytesReceived,
    .onSessionOpened = OnSessionOpened,
    .onSessionClosed = OnSessionClosed
};

经过查看OnSessionOpened和OnSessionClosed函数,发现并没有做任何处理,只是简单打印了函数被调用的日志。更多数据处理的细节在OnBytesReceived函数中,相关代码及说明如下:

/**
 * 函数功能:处理Session会话数据
 * 函数参数:
 *      sessionId:打开的Session会话id
 *      data:接收到的数据
 *      dataLen:接收到的数据长度
 */
void OnBytesReceived(int32_t sessionId, const void *data, uint32_t dataLen)
{
    CommuMessage commuMessage; // 将加收到的数据和数据长度封装为一个CommuMessage结构体对象
    commuMessage.payloadLength = dataLen;
    commuMessage.payload = data;
    int32_t errCode = ProcessCommuMsg(&commuMessage, &g_dmsFeatureCallback); // 调用ProcessCommuMsg处理接收到的数据
    HILOGI("[ProcessCommuMsg errCode = %d]", errCode);
}

将数据及其长度封装为一个CommuMessage对象,调用ProcessCommuMsg()对数据进行处理:

/**
 * 函数功能:解析Session会话数据并根据解析得到的结果执行相应的动作
 * 函数参数:
 *      commuMessage:封装后的Session会话数据
 *      dmsFeatureCallback:回调通知函数
 * 函数返回值:
 *      成功,返回0;否则返回非0
 * 描述:
 * 1.获取当前用户id,并进行权限检查
 * 2.检查参数的有效性
 * 3.对会话数据进行解析
 * 4.根据解析得到的命令id,执行相应的动作
 * 5.完成相应动作后,释放tlv数据链表所占的存储空间
 */
int32_t ProcessCommuMsg(const CommuMessage *commuMessage, const IDmsFeatureCallback *dmsFeatureCallback)
{
    if (!CanCall()) { // 通过getuid()获取当前用户id,对uid进行权限检查,如果没有权限,则不能调用,并返回调用失败错误码
        return DMS_EC_FAILURE;
    }
    if (commuMessage == NULL || commuMessage->payload == NULL || dmsFeatureCallback == NULL) { // 检查参数的有效性
        return DMS_EC_FAILURE;
    }
    TlvNode *tlvHead = NULL; // 声明一个TlvNode格式的头节点
    int32_t errCode = Parse(commuMessage->payload, commuMessage->payloadLength, &tlvHead); // 解析会话数据,并将数据存储为TlvNode格式的链表
    /* mainly for xts testsuit convenient, in non-test mode the onTlvParseDone should be set NULL */
    if (dmsFeatureCallback->onTlvParseDone != NULL) {
        dmsFeatureCallback->onTlvParseDone(errCode, tlvHead);
    }
    if (errCode != DMS_TLV_SUCCESS) {   // 如果errCode不为0,解析失败,返回3
        return DMS_EC_PARSE_TLV_FAILURE;
    }
    uint16_t commandId = UnMarshallUint16(tlvHead, DMS_TLV_TYPE_COMMAND_ID); // 将tlv链表中的数据转换为Unit16类型,得到对应的命令id
    HILOGI("[ProcessCommuMsg commandId %d]", commandId); // 将命令id打印至日志
    switch (commandId) { // 根据解析得到的命令id,执行相应的动作,目前支持两种命令id——DMS_MSG_CMD_START_FA(启动FA)和DMS_MSG_CMD_REPLY(回复消息)
        case DMS_MSG_CMD_START_FA: { // 远程启动FA
            errCode = StartAbilityFromRemoteHandler(tlvHead, dmsFeatureCallback->onStartAbilityDone);
            break;
        }
        case DMS_MSG_CMD_REPLY: { // 回复消息
            errCode = ReplyMsgHandler(tlvHead);
            break;
        }
        default: {
            HILOGW("[Unkonwn command id %d]", commandId);
            errCode = DMS_EC_UNKNOWN_COMMAND_ID;
            break;
        }
    }
    TlvFreeNodeList(tlvHead); // 释放链表所占空间
    return errCode;
}

首先检查用户的权限:

/**
 * 函数功能:对用户进行权限校验
 * 函数返回值:
 *      合法用户id,返回true;非法用户返回false 
 */
static bool CanCall()
{
#ifndef APP_PLATFORM_WATCHGT
    uid_t callerUid = getuid(); // 获取当前调用者的uid
    /* only foundation and xts (shell-enabled mode only) is reasonable to call ProcessCommuMsg directly */
    if (callerUid != FOUNDATION_UID && callerUid != SHELL_UID) { // 限制uid为FOUNDATION_UID(7)和SHELL_UID(2)拥有调用权限
        HILOGD("[Caller uid is not allowed, uid = %d]", callerUid);
        return false;
    }
#endif
    return true;
}

解析会话数据:

/**
 * 函数功能:解析会话数据,并存入TlvNode格式的链表中
 * 函数参数:
 *      payload:需要解析的数据
 *      length:数据的长度
 *      head:保存数据的TlvNode链表头节点
 * 函数返回值:
 *      成功,返回0;否则返回非0
 */
static int32_t Parse(const uint8_t *payload, uint16_t length, TlvNode **head)
{
    if (length > MAX_DMS_MSG_LENGTH) { // 如果数据长度大于指定的最大消息长度(1024),返回参数错误码(2)
        HILOGE("[Bad parameters][length = %d]", length);
        return DMS_TLV_ERR_PARAM;
    }
    TlvNode *tlvHead = NULL; // 初始化一个TlvNode头节点
    TlvErrorCode errCode = TlvBytesToNode(payload, length, &tlvHead); // 将byte数据保存在TlvNode链表中
    *head = tlvHead; // 返回链表的头节点
    HILOGI("[errCode = %d]", errCode);
    return errCode;
}

将会话数据保存为TlvNode格式:

/**
 * 函数功能:将tlv数据转换为TlvNode格式
 * 函数参数:
 *      byteBuffer:需要转换的数据报所在buffer
 *      bufLength:数据的长度
 *      tlv:TlvNode格式链表头节点
 * 函数返回值:转换成功返回0,否则返回非0
 */
TlvErrorCode TlvBytesToNode(const uint8_t *byteBuffer, uint16_t bufLength, TlvNode **tlv)
{
    if ((tlv == NULL) || (byteBuffer == NULL)) { // 检查参数的有效性
        HILOGE("[Bad parameter]");
        return DMS_TLV_ERR_PARAM;
    }
    /* bufLength is longer than TLV_TYPE_LEN + 1(means length should be at least 1 byte) */
    // 缓冲区长度应大于 TLV_TYPE_LEN + 1,即应至少为1字节
    if (bufLength <= (TLV_TYPE_LEN + 1)) {
        HILOGE("[Bad Length %d]", bufLength);
        return DMS_TLV_ERR_LEN;
    }
    TlvNode *head = MallocTlvNode(); // 声明一个TlvNode格式的头节点,并为其分配内存空间
    if (head == NULL) {
        return DMS_TLV_ERR_NO_MEM;
    }
    TlvNode *curNode = head;
    TlvNode *lastNode = curNode;
    /* translate bytes to tlv node until the end of buffer */
    // 将bytes数据转换为tlvNode格式数据
    uint16_t remainingLen = bufLength; // 剩余长度
    uint8_t handledNodeNum = 0; // 已处理的节点数
    const uint8_t *nodeStartAddr = byteBuffer; // nodeStartAddr指向byteBuffer首地址
    TlvErrorCode errCode = DMS_TLV_SUCCESS; // 默认errCode为0(转换成功)
    while (true) {
        uint16_t curTlvNodeLen = 0;
        errCode = TlvFillNode(nodeStartAddr, remainingLen, curNode, &curTlvNodeLen); // 填入byte数据到tlvNode节点
        BREAK_IF_FAILURE(errCode); // 检查errCode是否为0,如果不为0则跳出循环;这是一个宏定义代码块,在之后将会多次用到,猜想设计为宏的目的是为了减少代码量
        /* check whether there is need to continue processing the remaining nodes */
        handledNodeNum++;
        /* check node type sequence: the type of node must appear in strictly increasing order */
        // 检查节点type值,节点type必须严格按照递增顺序出现
        errCode = CheckNodeSequence(lastNode, curNode);
        BREAK_IF_FAILURE(errCode);
        remainingLen -= curTlvNodeLen;
        if (remainingLen == 0) {
            break;
        }
        HILOGD("[curNode length:%d type:%d handledNodeNum:%d]", curNode->length,
            curNode->type, handledNodeNum);
        /* if all is ok, then move to the T part of the next tlv node */
        nodeStartAddr += curTlvNodeLen;
        lastNode = curNode;
        errCode = MoveToNextTlvNode(&curNode); // 移动到下一个节点
        BREAK_IF_FAILURE(errCode);
    }
    if (errCode == DMS_TLV_SUCCESS && handledNodeNum < MIN_VALID_NODES) { // 如果转换成功但已处理的节点数量小于MIN_VALID_NODES(1),返回5
        HILOGE("[Parse done, but node num is invalid]");
        errCode = DMS_TLV_ERR_BAD_NODE_NUM;
    }
    if (errCode != DMS_TLV_SUCCESS) { // 如果errCode不为0,则释放tlv链表所占空间
        TlvFreeNodeList(head);
        head = NULL;
    }
    *tlv = head;
    return errCode;
}

/**
 * 函数功能:计算TLV格式数据的Length
 * 函数参数:
 *      bytesBuffer:byte数据所在buffer
 *      bufLength:byte数据长度
 *      length:计算出的长度
 *      bytesNumber:byte数据个数
 * 函数返回值:
 *      成功,返回0;否则返回非0
 */
static TlvErrorCode TlvBytesToLength(const uint8_t *bytesBuffer, uint16_t bufLength,
                                     uint16_t *length, uint8_t *bytesNumber)
{
    uint8_t bytesNum = 0;
    uint16_t len = 0;
    /* compute TLV's L, when value > 127, the L should be two bytes, else L is one byte long
       0-127      :  0b0xxxxxxx
       128-16383  :  0b1xxxxxxx 0b0xxxxxxx */
    // 计算L的大小,如果大于127,则为两个字节,否则为一个字节
    for (uint8_t i = 0; i < bufLength; i++) {
        TlvByteToLength(bytesBuffer[i], &len);
        bytesNum++;
        if (IsNextTlvLength(bytesBuffer[i])) { // 判断数据长度是否在有效范围内(128-16383)
            HILOGD("[bytesNum = %d, byte = %x]", bytesNum, bytesBuffer[i]);
            break;
        }
        if (bytesNum >= TLV_MAX_LENGTH_BYTES) {
            return DMS_TLV_ERR_LEN;
        }
    }
    /* it is meaningless to have a node with length being zero */
    // 长度为0的点无效
    if (len == 0) {
        HILOGE("[Invalid zero-size length]");
        return DMS_TLV_ERR_LEN;
    }
    *length = len;
    *bytesNumber = bytesNum;
    return DMS_TLV_SUCCESS;
}

将tlv数据填入链表节点中:

/**
 * 函数功能:将数据填入TlvNode节点中
 * 函数参数:
 *      byteBuffer:需要填入的数据
 *      bufLength:数据的长度
 *      node:需要填入的节点
 *      actualHandledLen:已经处理的长度
 * 函数返回值:
 *      成功,返回0;否则返回非0
 */
static TlvErrorCode TlvFillNode(const uint8_t *byteBuffer, uint16_t bufLength,
                                TlvNode *node, uint16_t *actualHandledLen)
{
    if (bufLength <= TLV_TYPE_LEN) { // 如果数据长度小于等于1,打印错误信息,返回3
        HILOGE("[Bad bufLength %d]", bufLength);
        return DMS_TLV_ERR_LEN;
    }
    /* fill TLV's T(type) */
    // 填入type值
    node->type = *byteBuffer;
    uint16_t curTlvNodeLen = TLV_TYPE_LEN;
    /* fill TLV's L(length) */
    // 填入length值
    uint16_t length = 0;
    uint8_t bytesNum = 0;
    const uint8_t *lengthPartAddr = byteBuffer + curTlvNodeLen; // 前面在TlvBytesToNode()函数中提到bufLength长度应为TLV_TYPE_LEN+1,所以这里会在byteBuffer之后加上一个字节
    TlvErrorCode errCode = TlvBytesToLength(lengthPartAddr, bufLength - curTlvNodeLen,
        &length, &bytesNum);
    if (errCode != DMS_TLV_SUCCESS) {
        return DMS_TLV_ERR_LEN;
    }
    node->length = length;
    curTlvNodeLen += bytesNum;
    /* fill TLV's V(value) */
    // 填入value值
    node->value = byteBuffer + curTlvNodeLen;
    curTlvNodeLen += length;
    if (curTlvNodeLen > bufLength) {
        return DMS_TLV_ERR_LEN;
    } else {
        *actualHandledLen = curTlvNodeLen;
    }
    return DMS_TLV_SUCCESS;
}

检查tlv链表节点顺序:

/**
 * 函数功能:检查节点type是否是递增顺序,保证数据按TLV格式保存
 * 函数参数:
 *      lastNode:最后一个节点
 *      curNode:当前节点
 * 函数返回值:
 *      满足递增条件,返回0;否则返回非0
 */
static inline TlvErrorCode CheckNodeSequence(const TlvNode *lastNode, const TlvNode *curNode)
{
    if (lastNode == curNode) {
        return DMS_TLV_SUCCESS;
    }
    if (lastNode->type >= curNode->type) {
        HILOGE("[Bad node type sequence '%d' is expected but '%d' appears]",
            lastNode->type, curNode->type);
        return DMS_TLV_ERR_OUT_OF_ORDER;
    }
    return DMS_TLV_SUCCESS;
}

总结

会话消息解析部分主要将byte数据保存为TlvNode格式的链表,并在转换过程中对数据做一些数据校验工作。

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请看下图提示:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值