一、概述
trans_service模块基于系统内核提供的socket通信,向authmanager模块提供设备认证通道管理和设备认证数据的传输;向业务模块提供session管理和基于session的数据收发功能,并且通过GCM模块的加密功能提供收发报文的加解密保护。 本文将继续介绍鸿蒙系统的会话机制的管理,承接上文 OpenHarmony源码分析之分布式软总线:trans_service模块(4)/TCP会话管理 的内容,本文将介绍鸿蒙系统如何处理客户端发起的请求消息。
二、源码分析
- 在上文提到的OnProcessDataAvailable()函数中,首先判断该会话的名称是不是"softbus_Lite_unknown",若是,则表示该会话是一个新建的会话,收到的消息应是客户端发起的请求消息,因此接下来调用HandleRequestMsg()函数进行处理,具体函数分析如下:
arduino
/*
函数功能:处理会话中的请求消息
函数参数:
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;
}
- 接着看函数HandleRequestMsg()的具体内容,首先接收数据包头部并解析,然后接收数据包负载部分,得到负载之后,在TransFirstPkg2Json()函数中将该负载解密然后转换为cjson格式的结构体。具体实现及分析如下:
arduino
/*
函数功能:将第一个数据包解密后转换为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;
}
DD一下: 欢迎大家关注公众号<程序猿百晓生>,可以了解到一下知识点。
erlang
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案)
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......
- 然后将接收到的json对象的值赋给当前会话,如会话名字sessionName以及会话密钥sessionKey,具体在AssignValue2Session()函数中实现,分析如下:
arduino
/*
函数功能:为当前会话赋值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;
}
- 最后回复给客户端,在ResponseToClient()函数中实现,具体分析如下:
scss
/*
函数功能:响应回复客户端的请求消息
函数参数:
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(),分析如下:
scss
/*
函数功能:构造回复消息,初始化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
}
至此,对于新会话的客户端请求数据的处理以及响应回复过程已结束。