往期知识点记录:
- 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总
- OpenHarmony分布式调度详解|dmslite.c
- OpenHarmony分布式调度详解|dmslite_feature.c 源码阅读
- OpenHarmony分布式调度详解|dmslite_session.c
- OpenHarmony分布式调度详解|dmslite_tlv_common.c
- OpenHarmony分布式调度详解|dmslite_famgr.c
- OpenHarmony分布式调度详解|dmslite_msg_handler.c
- OpenHarmony分布式调度详解|dmslite_pack.c
- OpenHarmony分布式调度详解|dmslite_permission.c
- OpenHarmony分布式调度详解|Service和Feature的定义和注册(1)
- OpenHarmony分布式调度详解|Service和Feature的定义和注册(2)
- 持续更新中……
前言
上一篇博客介绍了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🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请看下图提示: