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

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


一、概述

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 };    //读取接收缓冲区的数据保存到data数组中    int size = TcpRecvData(session->fd, data, AUTH_PACKET_HEAD_SIZE, 0);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;    }    //解析dataLen字段int dataLen = GetIntFromBuf(data, AUTH_PACKET_HEAD_SIZE - sizeof(int));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格式的结构体    cJSON *receiveObj = TransFirstPkg2Json(data, dataLen + AUTH_PACKET_HEAD_SIZE);    if (receiveObj == NULL) { return false;    }    //将接收到的json对象的值赋给当前会话,如会话名字sessionName以及会话密钥sessionKey    int ret = AssignValue2Session(session, receiveObj);    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;    }//获取数据包负载中的会话密钥索引(index)    int index = GetKeyIndex(buffer, AUTH_PACKET_HEAD_SIZE, MESSAGE_INDEX_LEN);    //通过索引(index)获取会话密钥    SessionKey *sessionKey = AuthGetSessionKeyByIndex(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);//将会话密钥赋值给加密密钥    //将密文的前IV_LEN个字节的值作为aes_gcm加密算法的初始iv值    ret += memcpy_s(cipherKey.iv, IV_LEN, cipherTxt, IV_LEN);    if (ret != 0) { free(firstDataJson); return NULL;    }//解密传输数据,解密成功则返回实际明文长度,并将明文存储在firstDataJson空间中    int plainLen = DecryptTransData(&cipherKey, cipherTxt, dataLen, (unsigned char*)firstDataJson, dataLen);    if (plainLen <= 0) { free(firstDataJson); return NULL;    }//json格式解析,将json字符串转换为cjson结构体    cJSON *receiveObj = cJSON_Parse(firstDataJson);    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;    }//获取"BUS_NAME"(key)字段的String类型的值(value)    char *recvBus = GetJsonString(receiveObj, "BUS_NAME");    if (recvBus == NULL) { return false;    }    if (strncpy_s(session->sessionName, NAME_LENGTH, recvBus, strlen(recvBus)) != 0) {    //将获取到的总线名BUS_NAME作为当前会话名sessionName return false;    }//获取"SESSION_KEY"(key)字段的String类型的值(value)    char *sessionKeyEncoded = GetJsonString(receiveObj, "SESSION_KEY");    if (sessionKeyEncoded == NULL) { return false;    }    size_t olen = 0;    //将base64编码的数据解码,然后赋值给当前的会话密钥session->sessionKey    int ret = mbedtls_base64_decode((unsigned char *)session->sessionKey, SESSION_KEY_LENGTH, &olen, (unsigned char *)sessionKeyEncoded, strlen(sessionKeyEncoded));    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对象中添加数值类型的"CODE"字段,值为1    cJSON_AddNumberToObject(jsonObj, "CODE", 1);    //向该cJSON对象中添加数值类型的"API_VERSION"字段,值为DEFAULT_API_VERSION    cJSON_AddNumberToObject(jsonObj, "API_VERSION", DEFAULT_API_VERSION);    DeviceInfo *local = BusGetLocalDeviceInfo();//获取本地设备信息    if (local == NULL) { return;    }    char* deviceId = local->deviceId;    //向该cJSON对象中添加数值类型的"DEVICE_ID"字段,值为deviceId    cJSON_AddStringToObject(jsonObj, "DEVICE_ID", deviceId);    //向该cJSON对象中添加数值类型的"UID"字段,值为-1    cJSON_AddNumberToObject(jsonObj, "UID", -1);    //向该cJSON对象中添加数值类型的"PID"字段,值为-1    cJSON_AddNumberToObject(jsonObj, "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}

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