OpenHarmony源码分析之分布式软总线:trans_service模块(5)/TCP会话管理

100 篇文章 2 订阅
100 篇文章 0 订阅

往期知识点记录:

一、概述

trans_service模块基于系统内核提供的socket通信,向authmanager模块提供设备认证通道管理和设备认证数据的传输;向业务模块提供session管理和基于session的数据收发功能,并且通过GCM模块的加密功能提供收发报文的加解密保护。 本文将继续介绍鸿蒙系统的会话机制的管理,承接上文 OpenHarmony源码分析之分布式软总线:trans_service模块(4)/TCP会话管理 的内容,本文将介绍鸿蒙系统如何处理客户端发起的请求消息。

二、源码分析

  1. 在上文提到的OnProcessDataAvailable()函数中,首先判断该会话的名称是不是”softbus_Lite_unknown”,若是,则表示该会话是一个新建的会话,收到的消息应是客户端发起的请求消息,因此接下来调用HandleRequestMsg()函数进行处理,具体函数分析如下:
/*
函数功能:处理会话中的请求消息
函数参数:
    session:会话地址
函数返回值:
    成功:返回true
    失败:返回false
详细:解析认证数据包头部
*/
static bool HandleRequestMsg(TcpSession *session)
{
    char data[RECIVED_BUFF_SIZE] = { 0 };
    int size = TcpRecvData(session->fd, data, AUTH_PACKET_HEAD_SIZE, 0);//读取接收缓冲区的数据保存到data数组中
    if (size != AUTH_PACKET_HEAD_SIZE) {//如果读到的数据小于认证数据包头部长度,返回错误
        return false;
    }
    int identifier = GetIntFromBuf(data, 0);//解析identifier字段
    if ((unsigned int)identifier != PKG_HEADER_IDENTIFIER) {//如果头部标识符不是PKG_HEADER_IDENTIFIER,则返回false
        return false;
    }
    int dataLen = GetIntFromBuf(data, AUTH_PACKET_HEAD_SIZE - sizeof(int));//解析dataLen字段
    if (dataLen + AUTH_PACKET_HEAD_SIZE >= RECIVED_BUFF_SIZE) {//越界
        return false;
    }
    int total = size;//记录读到的总量
    int remain = dataLen;//剩余未读
    while (remain > 0) {//循环读取套接字接收缓冲区,直到读满dataLen字节
        size = TcpRecvData(session->fd, data + total, remain, 0);
        remain -= size;
        total += size;
    }
    cJSON *receiveObj = TransFirstPkg2Json(data, dataLen + AUTH_PACKET_HEAD_SIZE);//将第一个数据包解密后转换为cjson格式的结构体
    if (receiveObj == NULL) {
        return false;
    }
    int ret = AssignValue2Session(session, receiveObj);//将接收到的json对象的值赋给当前会话,如会话名字sessionName以及会话密钥sessionKey
    cJSON_Delete(receiveObj);
    if (ret != true) {
        return false;
    }
    SessionListenerMap *sessionListener = GetSessionListenerByName(session->sessionName, strlen(session->sessionName));//根据会话名字在会话管理器中查找会话监听者
    if (sessionListener == NULL) {
        return false;
    }
    if (!ResponseToClient(session)) {//响应回复客户端的请求消息
        SOFTBUS_PRINT("[TRANS] HandleRequestMsg ResponseToClient fail\n");
        return false;
    }
    if (sessionListener->listener == NULL) {
        return false;
    }
    if (sessionListener->listener->onSessionOpened == NULL) {
        return false;
    }
    if (sessionListener->listener->onSessionOpened(session->fd) != 0) {
        return false;
    }
    return true;
}
  1. 接着看函数HandleRequestMsg()的具体内容,首先接收数据包头部并解析,然后接收数据包负载部分,得到负载之后,在TransFirstPkg2Json()函数中将该负载解密然后转换为cjson格式的结构体。具体实现及分析如下:
/*
函数功能:将第一个数据包解密后转换为cjson格式的结构体
函数参数:
    buffer:数据包起始地址
    bufferSize:数据包总大小
函数返回值:
    成功:返回cJSON格式的结构体
    失败:返回NULL
详细:
*/
static cJSON *TransFirstPkg2Json(const char *buffer, int bufferSize)
{
    if (bufferSize < AUTH_PACKET_HEAD_SIZE) {//数据包大小小于头部长度
        SOFTBUS_PRINT("[TRANS] bufferSize < AUTH_PACKET_HEAD_SIZE\n");
        return NULL;
    }
    int offset = AUTH_PACKET_HEAD_SIZE - sizeof(int);//为了解析获取dataLen字段
    int dataLen = GetIntFromBuf(buffer, offset) - SESSION_KEY_INDEX_SIZE;//除去会话密钥索引的数据长度
    if (dataLen <= 0 || dataLen > (RECIVED_BUFF_SIZE - AUTH_PACKET_HEAD_SIZE)) {//越界检查
        return NULL;
    }
    int index = GetKeyIndex(buffer, AUTH_PACKET_HEAD_SIZE, MESSAGE_INDEX_LEN);//获取数据包负载中的会话密钥索引(index)
    SessionKey *sessionKey = AuthGetSessionKeyByIndex(index);//通过索引(index)获取会话密钥
    if (sessionKey == NULL) {
        SOFTBUS_PRINT("[TRANS] TransFirstPkg2Json GetSessionKey fail\n");
        return NULL;
    }
    char *firstDataJson = calloc(1, dataLen);//申请dataLen字节数的内存空间
    if (firstDataJson == NULL) {
        return NULL;
    }
    unsigned char* cipherTxt = (unsigned char*)(buffer + AUTH_PACKET_HEAD_SIZE + SESSION_KEY_INDEX_SIZE);//得到密文的首地址
    AesGcmCipherKey cipherKey = {0};//aes_gcm密钥
    cipherKey.keybits = GCM_KEY_BITS_LEN_128;//密钥长度为128位
    int ret = memcpy_s(cipherKey.key, SESSION_KEY_LENGTH, sessionKey->key, AUTH_SESSION_KEY_LEN);//将会话密钥赋值给加密密钥
    ret += memcpy_s(cipherKey.iv, IV_LEN, cipherTxt, IV_LEN);//将密文的前IV_LEN个字节的值作为aes_gcm加密算法的初始iv值
    if (ret != 0) {
        free(firstDataJson);
        return NULL;
    }
    int plainLen = DecryptTransData(&cipherKey, cipherTxt, dataLen, (unsigned char*)firstDataJson, dataLen);//解密传输数据,解密成功则返回实际明文长度,并将明文存储在firstDataJson空间中
    if (plainLen <= 0) {
        free(firstDataJson);
        return NULL;
    }
    cJSON *receiveObj = cJSON_Parse(firstDataJson);//json格式解析,将json字符串转换为cjson结构体
    free(firstDataJson);
    return receiveObj;
}
  1. 然后将接收到的json对象的值赋给当前会话,如会话名字sessionName以及会话密钥sessionKey,具体在AssignValue2Session()函数中实现,分析如下:
/*
函数功能:为当前会话赋值sessionName和sessionKey,其值来自于对端发送的json数据中
函数参数:
    session:TCP会话结构体
    receiveObj:cJSON结构体对象
函数返回值:
    成功:返回cJSON格式的结构体
    失败:返回NULL
详细:
*/
static bool AssignValue2Session(TcpSession *session, cJSON *receiveObj)
{
    if (receiveObj == NULL) {
        return false;
    }
    char *recvBus = GetJsonString(receiveObj, "BUS_NAME");//获取"BUS_NAME"(key)字段的String类型的值(value)
    if (recvBus == NULL) {
        return false;
    }
    if (strncpy_s(session->sessionName, NAME_LENGTH, recvBus, strlen(recvBus)) != 0) {//将获取到的总线名BUS_NAME作为当前会话名sessionName
        return false;
    }
    char *sessionKeyEncoded = GetJsonString(receiveObj, "SESSION_KEY");//获取"SESSION_KEY"(key)字段的String类型的值(value)
    if (sessionKeyEncoded == NULL) {
        return false;
    }
    size_t olen = 0;
    int ret = mbedtls_base64_decode((unsigned char *)session->sessionKey, SESSION_KEY_LENGTH,
        &olen, (unsigned char *)sessionKeyEncoded, strlen(sessionKeyEncoded));//将base64编码的数据解码,然后赋值给当前的会话密钥session->sessionKey
    if (ret != 0) {
        SOFTBUS_PRINT("[TRANS] AssignValue2Session mbedtls_base64_decode error: %d\n", ret);
        return false;
    }
    SOFTBUS_PRINT("[TRANS] AssignValue2Session busname=%s, fd=%d\n", session->sessionName, session->fd);
    return true;
}
  1. 最后回复给客户端,在ResponseToClient()函数中实现,具体分析如下:
/*
函数功能:响应回复客户端的请求消息
函数参数:
    session:TCP会话结构体
函数返回值:
    成功:返回true
    失败:返回false
详细:
*/
static bool ResponseToClient(TcpSession *session)
{
    cJSON *jsonObj = cJSON_CreateObject();//产生一个cJSON对象
    if (jsonObj == NULL) {
        return false;
    }
    GetReplyMsg(jsonObj, session);//构造回复消息,初始化cJSON对象
    char *msg = cJSON_PrintUnformatted(jsonObj);//将cJSON对象输出为无格式的字符串
    if (msg == NULL) {
        cJSON_Delete(jsonObj);
        return false;
    }
    int bufLen = 0;
    unsigned char *buf = PackBytes(msg, &bufLen);//按字节构造认证协议数据包,返回数据包缓冲区首地址
    if (buf == NULL) {
        SOFTBUS_PRINT("[TRANS] ResponseToClient PackBytes fail\n");
        free(msg);
        cJSON_Delete(jsonObj);
        return false;
    }
    int dataLen = TcpSendData(session->fd, (char*)buf, bufLen, 0);//发送数据给对端
    free(msg);
    cJSON_Delete(jsonObj);
    free(buf);
    if (dataLen <= 0) {
        SOFTBUS_PRINT("[TRANS] ResponseToClient TcpSendData fail\n");
        return false;
    }
    return true;
}

其中构造回复消息的函数为GetReplyMsg(),分析如下:

/*
函数功能:构造回复消息,初始化cJSON对象
函数参数:
    jsonObj:cJSON对象
    session:当前TCP会话
函数返回值:无
详细:
*/
static void GetReplyMsg(cJSON *jsonObj, const TcpSession *session)
{
    cJSON_AddNumberToObject(jsonObj, "CODE", 1);//向该cJSON对象中添加数值类型的"CODE"字段,值为1
    cJSON_AddNumberToObject(jsonObj, "API_VERSION", DEFAULT_API_VERSION);//向该cJSON对象中添加数值类型的"API_VERSION"字段,值为DEFAULT_API_VERSION
    DeviceInfo *local = BusGetLocalDeviceInfo();//获取本地设备信息
    if (local == NULL) {
        return;
    }
    char* deviceId = local->deviceId;
    cJSON_AddStringToObject(jsonObj, "DEVICE_ID", deviceId);//向该cJSON对象中添加数值类型的"DEVICE_ID"字段,值为deviceId
    cJSON_AddNumberToObject(jsonObj, "UID", -1);//向该cJSON对象中添加数值类型的"UID"字段,值为-1
    cJSON_AddNumberToObject(jsonObj, "PID", -1);//向该cJSON对象中添加数值类型的"PID"字段,值为-1
    cJSON_AddStringToObject(jsonObj, "PKG_NAME", session->sessionName);
    cJSON_AddStringToObject(jsonObj, "CLIENT_BUS_NAME", session->sessionName);
    cJSON_AddStringToObject(jsonObj, "AUTH_STATE", "");//认证状态为空
    cJSON_AddNumberToObject(jsonObj, "CHANNEL_TYPE", 1);//CHANNEL_TYPE为1
}

至此,对于新会话的客户端请求数据的处理以及响应回复过程已结束。

写在最后

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值