文章目录
软总线
本文简单介绍softbus_lite的部分实现。代码来自openharmony 3.0 foundation/commucation/softbus_lite。
softbus_lite是L0和L1设备所采用的软总线的实现,相比于标准设备的dsoftbus,功能有所减少。例如softbus_lite只实现被动接收Session连接的功能,无法主动发起session连接以及发现服务。
softbus_lite更多的应用场景是作为边缘设备,通过在局域网内发布服务,等待标准设备的订阅。
一、会话传输
L0,L1系统属于lite系统,无法主动打开session,只能创建session server,然后等待远程设备来打开会话。在会话回调函数中,可获取会话id。通过session id可用来发送和接收数据。
1.1、CreateSessionServer
CreateSessionServer:session server的作用只是用来管理listenerMap,即监听的功能,回调函数的执行。
struct ISessionListener {
//当会话被打开时回调,sessionId表示本地会话id,也就是tcp连接的句柄,发送和接收数据时会使用到
int (*onSessionOpened)(int sessionId);
void (*onSessionClosed)(int sessionId);
//接受到会话的数据
void (*onBytesReceived)(int sessionId, const void *data, unsigned int dataLen);
};
//创建g_sessionMgr,保存参数信息到全局数组
int CreateSessionServer(const char* moduleName, const char* sessionName, struct ISessionListener *listener)
{
int ret = CreateSessionServerInner(moduleName, sessionName, listener);
}
//将listener保存到全局数组
static int CreateSessionServerInner(const char* moduleName, const char* sessionName, struct ISessionListener *listener)
{
//创建g_sessionMgr
if (g_sessionMgr == NULL && InitGSessionMgr() != 0) {
return TRANS_FAILED;
}
//在serverListenerMap中找一个空位置
int findIndex = -1;
for (int i = 0; i < MAX_SESSION_SERVER_NUM; i++) {
if (g_sessionMgr->serverListenerMap[i] == NULL) {
findIndex = i;
break;
}
}
if (findIndex >= 0 && findIndex < MAX_SESSION_SERVER_NUM) {
g_sessionMgr->serverListenerMap[findIndex] = calloc(1, sizeof(SessionListenerMap));
//把参数信息保存到数组
SessionListenerMap *listenerMap = g_sessionMgr->serverListenerMap[findIndex];
if (strncpy_s(listenerMap->sessionName, NAME_LENGTH, sessionName, strlen(sessionName)) ||
strncpy_s(listenerMap->moduleName, NAME_LENGTH, moduleName, strlen(moduleName))) {
free(listenerMap);
listenerMap = NULL;
return TRANS_FAILED;
}
//把回调函数保存到全局数组
listenerMap->listener = listener;
}
return 0;
}
1.2、SendBytes
向会话对端发送字节数据,实际上是调用socket接口 进行发送。
sessionfd就是通过回调函数获取到的sessionid,其实就是tcp端口的句柄。
int SendBytes(int sessionfd, const unsigned char *buf, unsigned int size)
{
//获取session
TcpSession *session = GetSessionById(sessionfd);
//打包数据到cipherBuf
char *cipherBuf = (char *)TransPackBytes(session, buf, size, &cipherLen);
int32_t bytes = 0;
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(sessionfd, &writefds);
struct timeval msTimeout;
//设置超时时间
msTimeout.tv_sec = DEFAULT_TIMEOUT / ONE_SEC;
msTimeout.tv_usec = (DEFAULT_TIMEOUT % ONE_SEC) * ONE_SEC;
//阻塞等待socket就绪
int err = select(sessionfd + 1, NULL, &writefds, NULL, &msTimeout);
//使用socket发送数据
while (FD_ISSET(sessionfd, &writefds) && bytes < (int32_t)cipherLen && (sessionfd) >= 0) {
errno = 0;
int32_t rc = send(sessionfd, cipherBuf + bytes, cipherLen - bytes, 0);
if ((rc == -1) && (errno == EAGAIN)) {
continue;
} else if (rc <= 0) {
if (bytes == 0) {
bytes = -1;
}
break;
}
bytes += rc;
}
free(cipherBuf);
return 0;
}
二、设备发现
用户使用发现功能来自动发现周围的OpenHarmony设备时,需要保证发现端设备与被发现端设备在同一个局域网内,并且互相能收到对方以下流程的报文。
(1)发现端设备,发起discover请求后,使用coap协议在局域网内发送广播。
(2)被发现端设备使用PublishService接口发布服务,接收端收到广播后,发送coap协议单播给发现端。
(3)发现端设备收到报文会更新设备信息。
设备发现的最终目的就是发现局域网内的设备,更新周围的设备id。
目前L0、L1系统,也就是lite系统,不支持作为发现端。只能作为被发现端。通过使用PublishService接口发布服务,就能被标准设备发现。
发现服务的工作原理是这样的:
-
首先由本地A设备,远程B设备。A设备调用PublishService发布服务,会创建一个udp服务器,监听一个广播端口。
-
B设备调用StartDiscovery开始发现设备,会创建一个udp客户端,向局域网广播。(B是L2设备)
-
A收到B的广播消息后,创建一个udp客户端,给B发送响应消息,响应消息中包含A的设备信息。
-
B收到响应后,会执行回调函数,函数参数就是A的设备信息。
-
B接下来可对A进行会话连接等。
下面的结构体表示服务提供的设备信息:这个信息会一直传递到对端设备
/**
* @brief 发送给对端的本地设备信息
*/
typedef struct PublishInfo {
//服务id,如何确定?
int publishId;
//L0,L1设备只能是DISCOVER_MODE_PASSIVE 被动发现
int mode;
//协议:COAP协议
ExchangeMedium medium;
//报文发送频率
ExchangeFreq freq;
/** 服务发布的能力. 见下文 */
const char *capability;
/** 服务的数据。见下文 */
unsigned char *capabilityData;
/** Maximum length of the capability data for service publishing (2 bytes) */
unsigned int dataLen;
} PublishInfo;
/**
* @brief 服务的能力枚举,具体是什么意思?
*/
typedef enum {
/** MeeTime */
HICALL_CAPABILITY_BITMAP = 0,
/** Video reverse connection in the smart domain */
PROFILE_CAPABILITY_BITMAP = 1,
/** Gallery in Vision */
HOMEVISIONPIC_CAPABILITY_BITMAP = 2,
/** cast+ */
CASTPLUS_CAPABILITY_BITMAP,
/** Input method in Vision */
AA_CAPABILITY_BITMAP,
/** Device virtualization tool package */
DVKIT_CAPABILITY_BITMAP,
/** Distributed middleware */
DDMP_CAPABILITY_BITMAP
} DataBitMap;
//将服务的能力和字符串绑定
static const CapabilityMap g_capabilityMap[] = {
{HICALL_CAPABILITY_BITMAP, (char *)"hicall"},
{PROFILE_CAPABILITY_BITMAP, (char *)"profile"},
{CASTPLUS_CAPABILITY_BITMAP, (char *)"castPlus"},
{HOMEVISIONPIC_CAPABILITY_BITMAP, (char *)"homevisionPic"},
{AA_CAPABILITY_BITMAP, (char *)"aaCapability"},
{DVKIT_CAPABILITY_BITMAP, (char *)"dvKit"},
{DDMP_CAPABILITY_BITMAP, (char *)"ddmpCapability"},
};
2.1、发布服务
即设备在局域网内发布上述的服务,这些服务就能被发现和调用。其本质就是将PublishInfo保存到全局变量中,并创建coap server监听端口,等待服务被发现和调用。当收到发现报文时,将PublishInfo里的信息通过coap报文,发送给发现者。
PublishService用于发布服务,代码如下:
int PublishService(const char *moduleName, const struct PublishInfo *info, const struct IPublishCallback *cb)
{
//检查是否有软总线权限
if (SoftBusCheckPermission(SOFTBUS_PERMISSION) != 0 || info == NULL || cb == NULL) {
return ERROR_INVALID;
}
//初始化全局服务
if (InitService() != ERROR_SUCCESS) {
return ERROR_FAIL;
}
//初始化module
PublishModule *findModule = AddPublishModule(moduleName, info);
//设置全局变量
if (info->capability == NULL || info->capabilityData == NULL) {
(void)CoapRegisterDefualtService();
} else {
ret = DoRegistService(info->medium);
}
//执行用户回调函数
if (ret != ERROR_SUCCESS) {
PublishCallback(info->publishId, PUBLISH_FAIL_REASON_UNKNOWN, findModule, cb);
return ERROR_FAIL;
} else {
//call back
PublishCallback(info->publishId, ERROR_SUCCESS, findModule, cb);
return ERROR_SUCCESS;
}
}
初始化全局服务,是主要的初始化代码:
//初始化服务:初始化设备信息ip地址、注册wifi回调函数、coap初始化。在连接到wifi后,启动soft bus、注册设备信息到softbus
int InitService(void)
{
//初始化 g_deviceInfo、g_publishModule、g_capabilityData、g_wifiCallback等全局变量
InitCommonManager();
g_publishModule = calloc(1, sizeof(PublishModule) * MAX_MODULE_COUNT);
g_capabilityData = calloc(1, MAX_SERVICE_DATA_LEN);
//注册wifi事件回调函数,具体见下文
RegisterWifiCallback(WifiEventTrigger);
//初始化coap服务器,处理coap报文逻辑
int ret = CoapInit();
//给wifi队列写入消息,本质是使能WifiEventTrigger()函数
CoapWriteMsgQueue(UPDATE_IP_EVENT);
//设置设备信息
ret = CoapRegisterDeviceInfo();
return ERROR_SUCCESS;
}
在整个发现服务流程中,以下全局变量经常使用到,有必要简单的了解下
//全局模块 对应一个上层的应用
typedef struct {
char package[MAX_PACKAGE_NAME]; //上层应用packagename
int publishId;
unsigned short medium;
unsigned short capabilityBitmap;
char *capabilityData;
unsigned short dataLength;
unsigned short used;
} PublishModule;
g_publishModule;
char *g_capabilityData = NULL;
//设备信息
typedef struct DeviceInfo {
char deviceName[MAX_DEV_NAME_LEN];
char deviceId[MAX_DEV_ID_LEN];
char deviceIp[MAX_DEV_IP_LEN];
char version[MAX_DEV_VERSION_LEN];
char softwareVersion[MAX_SOFTWARE_VERSION_LEN]; //软件版本
char networkName[MAX_DEV_NETWORK_LEN];
int deviceType;
int devicePort;
NetworkState networkState;
int isAccountTrusted;
} DeviceInfo;
g_deviceInfo;
2.1.1、wifi事件
WifiEventTrigger()是一个回调函数,他的执行环境是线程CoapWifiEventThread(待会解释)。当连接到wifi后,CoapWifiEventThread就会调用WifiEventTrigger(),其参数para=1,表示连接到wifi。表示网络已连接,那么就可用开始软总线。
//WIFI事件回调 state=1:UPDATE_IP_EVENT
void WifiEventTrigger(unsigned int para)
{
DeviceInfo *localDev = GetCommonDeviceInfo();
//para=state=1 连接上wifi
if (para) {
//获取ip
char wifiIp[MAX_DEV_IP_LEN] = {0};
CoapGetIp(wifiIp, MAX_DEV_IP_LEN, 0);
if (strcmp(wifiIp, "0.0.0.0") == 0) {
return;
}
ret = memcpy_s(localDev->deviceIp, sizeof(localDev->deviceIp), wifiIp, sizeof(wifiIp));
} else {
//清除ip,断开网络连接
ret = memset_s(localDev->deviceIp, sizeof(localDev->deviceIp), 0, sizeof(localDev->deviceIp));
}
//开启软总线
if (BusManager(para) != ERROR_SUCCESS) {
return;
}
//初始化本地设备信息
if (CoapRegisterDeviceInfo() != ERROR_SUCCESS) {
return;
}
//注册capablity 和 g_capabilityData 到nstackx
if (DoRegistService(COAP) != ERROR_SUCCESS) {
return;
}
}
2.1.2、coap服务器
coapInit()最终是调用CoapInitDiscovery() 来建立coap协议所需的资源:
- udp server:用于监听coap报文
- wifi消息队列:缓存wifi状态消息
- CoapWifiEventThread:处理wifi消息队列
- CreateCoapListenThread:处理coap报文交互逻辑
//初始化coap协议所需资源
int CoapInitDiscovery(void)
{
//建立udp server 监听5684端口(coap协议默认端口)
int ret = CoapInitSocket();
//创建wifi消息队列
ret = CoapInitWifiEvent();
//创建 CoapWifiEventThread 线程
if (CreateMsgQueThread() != NSTACKX_EOK) {
return NSTACKX_EFAILED;
}
//创建CoapReadHandle 线程
return CreateCoapListenThread();
}
CoapWifiEventThread线程:其本质内容就是执行wifi事件回调函数。
//处理wifi队列的消息
void CoapWifiEventThread(unsigned int uwParam1, unsigned int uwParam2, unsigned int uwParam3, unsigned int uwParam4)
{
g_wifiTaskStart = 1;
while (g_wifiTaskStart) {
//读取队列消息的消息: handler(WifiEventTrigger)
ret = ReadMsgQue(g_wifiQueueId, &handle, &readSize);
if ((ret == 0) && (readSize == sizeof(AddressEventHandler))) {
if (handle.handler == NULL) {
continue;
}
//执行回调函数(WifiEventTrigger)
handle.handler(handle.state);
}
}
}
CoapReadHandle:读取udp server接收的数据,并处理数据。
static void CoapReadHandle(unsigned int uwParam1, unsigned int uwParam2, unsigned int uwParam3, unsigned int uwParam4)
{
//获取之间创建的udp server
int serverFd = GetCoapServerSocket();
while (g_terminalFlag) {
FD_ZERO(&readSet);
FD_SET(serverFd, &readSet);
//读取udp server接收的数据
ret = select(serverFd + 1, &readSet, NULL, NULL, NULL);
if (ret > 0) {
if (FD_ISSET(serverFd, &readSet)) {
//处理接收数据
HandleReadEvent(serverFd);
}
} else {
SOFTBUS_PRINT("[DISCOVERY]ret:%d,error:%d\n", ret, errno);
}
}
}
处理udp server数据的代码:
//处理udp数据报事件
static void HandleReadEvent(int fd)
{
int socketFd = fd;
unsigned char *recvBuffer = calloc(1, COAP_MAX_PDU_SIZE + 1);
ssize_t nRead;
//读取coap报文
nRead = CoapSocketRecv(socketFd, recvBuffer, COAP_MAX_PDU_SIZE);
COAP_Packet decodePacket;
(void)memset_s(&decodePacket, sizeof(COAP_Packet), 0, sizeof(COAP_Packet));
decodePacket.protocol = COAP_UDP;
//解析coap报文的数据,封装成decodePacket
COAP_SoftBusDecode(&decodePacket, recvBuffer, nRead);
//响应远程发现者
PostServiceDiscover(&decodePacket);
free(recvBuffer);
}
响应的内容是什么?具体来看PostServiceDiscover():
从对端发来的coap报文中,可解析出对端的ip地址和url,然后将本地设备信息和对端地址组成coap报文,发送给对端
//给远程设备发送响应
void PostServiceDiscover(const COAP_Packet *pkt)
{
char *remoteUrl = NULL;
//从pkt中,可获取对端设备的ip等信息,封装成deviceinfo
DeviceInfo deviceInfo;
(void)memset_s(&deviceInfo, sizeof(deviceInfo), 0, sizeof(deviceInfo));
//解析pkt->payload.buffer数据到deviceInfo
if (GetServiceDiscoverInfo(pkt->payload.buffer, pkt->payload.len, &deviceInfo, &remoteUrl) != NSTACKX_EOK) {
return;
}
//获取对端的ip地址
char wifiIpAddr[NSTACKX_MAX_IP_STRING_LEN];
(void)memset_s(wifiIpAddr, sizeof(wifiIpAddr), 0, sizeof(wifiIpAddr));
(void)inet_ntop(AF_INET, &deviceInfo.netChannelInfo.wifiApInfo.ip, wifiIpAddr, sizeof(wifiIpAddr));
if (remoteUrl != NULL) {
//利用deviceinfo,发送coap报文给对端
CoapResponseService(pkt, remoteUrl, wifiIpAddr);
free(remoteUrl);
}
}
//发送CoapRequest到remoteIp
static int CoapResponseService(const COAP_Packet *pkt, const char* remoteUrl, const char* remoteIp)
{
int ret;
//初始化CoapRequest
CoapRequest coapRequest;
coapRequest.remoteUrl = remoteUrl; //资源地址
coapRequest.remoteIp = remoteIp; //ip地址
//payload包含了本地设备的信息
char *payload = PrepareServiceDiscover();
//以下就是构建coapRequest
COAP_ReadWriteBuffer sndPktBuff = {0};
sndPktBuff.readWriteBuf = calloc(1, COAP_MAX_PDU_SIZE);
sndPktBuff.size = COAP_MAX_PDU_SIZE;
sndPktBuff.len = 0;
//根据pkt和ip构建coap报文sndPktBuff
ret = BuildSendPkt(pkt, remoteIp, payload, &sndPktBuff);
free(payload);
coapRequest.data = sndPktBuff.readWriteBuf;
coapRequest.dataLength = sndPktBuff.len;
//创建udp客户端并发送coap报文
ret = CoapSendRequest(&coapRequest);
free(sndPktBuff.readWriteBuf);
sndPktBuff.readWriteBuf = NULL;
return ret;
}
payload的内容就是设备信息,其json格式如下:
{
"deviceId":[device ID, string],
"deviceName":[device name, string],
"type": [device type, number],
"version":[hicom version, string],
"wlanIp":[WLAN IP address, string],
"capabilityBitmap":[bitmap, bitmap, bitmap, ...]
"coapUri":[coap uri for discover, string] <-- optional. When present, means it's broadcast request.
}
对端在收到这个消息后,就可以会触发回调函数:该函数在dsoftbus中定义
void (*OnDeviceFound)(const DeviceInfo *device);
该函数的参数device中就包含了payload中的信息,这样对端就了解了局域网内有什么设备。
2.2、软总线
上一节的WifiEventTrigger()中,在连接到wifi后,会启动软总线,创建软总线运行所需的资源:
- 回调函数OnConnectEvent:初始化认证相关的结构体
- 回调函数OnDataEvent:认证数据的处理逻辑(详)
- WaitProcess线程:读取g_listenFd,执行回调函数,处理软总线的事务
- SelectSessionLoop线程:处理tcp session的事务
- AuthManager:设备认证事务
int StartBus(void)
{
//设置软总线的回调函数
g_baseLister.onConnectEvent = OnConnectEvent;
g_baseLister.onDataEvent = OnDataEvent;
//创建 tcp server、WaitProcess线程 用于执行回调函数
int authPort = StartListener(&g_baseLister, info->deviceIp);
//创建tcp server、SelectSessionLoop线程、用于实现session的回调
int sessionPort = StartSession(info->deviceIp);
//保存两个tcp server的端口
AuthMngInit(authPort, sessionPort);
g_busStartFlag = 1;
}
2.2.1、SelectSessionLoop
SelectSessionLoop线程:监听所有session的数据,并处理这些数据。
session是一个建立在tcp传输上的概念,创建一个session的过程如下:
首先A创建一个tcp server,监听连接。B与A建立TCP连接,能够正常传输TCP数据。接着发送特定的TCP数据,建立session连接。所以这里涉及到了两个连接,一个是tcp层的,一个session层的连接。tcp 层的连接建立完成后可以初始化session结构体,其实现在ProcessConnection().Session层的连接在ProcessSesssionData()中完成。
static void SelectSessionLoop(TcpSessionMgr *tsm)
{
tsm->isSelectLoopRunning = true;
while (true) {
fd_set readfds; //用于读取数据
fd_set exceptfds;
//等待readfds的消息
int ret = select(maxFd + 1, &readfds, NULL, &exceptfds, NULL);
//处理数据
ProcessData(tsm, &readfds);
}
tsm->isSelectLoopRunning = false;
}
//处理session数据
static void ProcessData(TcpSessionMgr *tsm, fd_set *rfds)
{
//listenFd的消息,则表示需要创建tcp session连接
if (FD_ISSET(tsm->listenFd, rfds)) {
ProcessConnection(tsm);
return;
}
//否则,是tcp session数据
ProcessSesssionData(tsm, rfds);
}
ProcessConnection:完成tcp连接,并初始化session结构体,方便下一阶段建立session使用。
static void ProcessConnection(TcpSessionMgr *tsm)
{
//建立tcp 连接
int cfd = accept(tsm->listenFd, (struct sockaddr *)&addr, &addrLen);
//创建tcp session
TcpSession *session = CreateTcpSession();
//把authConn的deivceid复制到session
AuthConn* authConn = GetOnLineAuthConnByIp(inet_ntoa(addr.sin_addr));
if (authConn != NULL && strncpy_s(session->deviceId, MAX_DEV_ID_LEN, authConn->deviceId,
strlen(authConn->deviceId)) != 0) {
SOFTBUS_PRINT("[TRANS] Error on copy deviceId of session.");
free(session);
CloseSession(cfd);
return;
}
//把tcp连接添加session
session->fd = cfd;
//把session给sessionmgr管理
int result = AddSession(tsm, session);
return;
}
ProcessSesssionData:初始化完成session后,接收到session数据。有两种session数据类型,一种是请求建立session,一种是正常的session数据。
static void ProcessSesssionData(const TcpSessionMgr *tsm, const fd_set *rfds)
{
//遍历所有的tcp session,找到fd对应的session
for (int i = 0; i < MAX_SESSION_SUM_NUM; i++) {
if (tsm->sessionMap_[i] != NULL && tsm->sessionMap_[i]->fd != -1 &&
FD_ISSET(tsm->sessionMap_[i]->fd, rfds) > 0) {
//处理对应session数据
if (!OnProcessDataAvailable(tsm->sessionMap_[i])) {
return;
}
}
}
}
//处理tcp session的数据
static bool OnProcessDataAvailable(TcpSession *session)
{
//name是softbus_Lite_unknown 说明是对方发起session连接请求
if (strcmp(session->sessionName, "softbus_Lite_unknown") == 0) {
//处理session连接请求,响应请求,执行回调函数onSessionOpened
bool isSuccess = HandleRequestMsg(session);
if (!isSuccess) {
CloseSession(session->fd);
}
return isSuccess;
} else {
//已经建立了session 正常接收数据
unsigned char* buf = calloc(1, RECIVED_BUFF_SIZE);
SessionListenerMap *sessionListener = GetSessionListenerByName(session->sessionName,
strlen(session->sessionName));
if (sessionListener != NULL && sessionListener->listener != NULL) {
//读取session数据
int recvLen = TcpSessionRecv(session, (char *)buf, RECIVED_BUFF_SIZE, 0);
//执行session的回调函数,处理 接收数据
sessionListener->listener->onBytesReceived(session->fd, buf, recvLen);
free(buf);
return true;
}
free(buf);
}
}
2.2.2、WaitProcess
WaitProcess线程,他的任务是监听g_listenFd,并处理软总线上的回调函数。
//等待建立socket,并回调成功建立
static void WaitProcess(void)
{
while (1) {
//等待g_listenFd 的数据
int ret = select(g_maxFd + 1, &readSet, NULL, &readSet, NULL);
if (ret > 0) {
//处理g_listenFd的数据
if (!ProcessAuthData(g_listenFd, &readSet)) {
StopListener();
break;
}
}
}
}
//处理softbus数据 建立socket 回调g_baseLister
static bool ProcessAuthData(int listenFd, const fd_set *readSet)
{
//listenFd是否在readSet,即listenFd是否有数据
if (FD_ISSET(listenFd, readSet)) {
struct sockaddr_in addrClient = {0};
socklen_t addrLen = sizeof(addrClient);
//建立tcp连接
g_dataFd = accept(listenFd, (struct sockaddr *)(&addrClient), &addrLen);
//更新g_dataFd
RefreshMaxFd(g_dataFd);
//执行回调函数: 连接成功 OnConnectEvent
if (g_callback->onConnectEvent(g_dataFd, inet_ntoa(addrClient.sin_addr)) != 0) {
CloseAuthSessionFd(g_dataFd);
}
}
//执行回调函数:数据接收 OnDataEvent
if (g_dataFd > 0 && FD_ISSET(g_dataFd, readSet)) {
g_callback->onDataEvent(g_dataFd);
}
return true;
}
2.2.3、OnConnectEvent
重要的结构体,负责认证的连接
typedef struct AuthConn {
int fd;
char authId[MAX_AUTH_ID_LEN]; //?
char deviceId[MAX_DEV_ID_LEN];
char deviceIp[MAX_DEV_IP_LEN];
int busVersion;
int authPort; //软总线上的认证tcp端口
int sessionPort; //软总线上的session端口
int authState;
int onlineState;
DataBuffer db;
} AuthConn;
软总线在收到连接事件后,回调OnDataEvent(),设置aconn对象的ip和fd。aconn会在OnDataEvent()中大放异彩
//将fd、ip赋值给对应的AuthConn
void ProcessConnectEvent(int fd, const char *ip)
{
SOFTBUS_PRINT("[AUTH] ProcessConnectEvent fd = %d\n", fd);
if (fd < 0 || ip == NULL) {
return;
}
//获取 aconn,若不为NULL,说明已经认证过了
AuthConn *aconn = FindAuthConnByFd(fd);
if (aconn != NULL) {
CloseConn(aconn);
return;
}
//还未认证,设置aconn
aconn = calloc(1, sizeof(AuthConn));
//复制ip、fd给aconn
int ret = strcpy_s(aconn->deviceIp, sizeof(aconn->deviceIp), ip);
aconn->fd = fd;
//把 aconn 添加到 g_fdMap 数组
ret = AddAuthConnToList(aconn);
}
2.2.4、OnDataEvent
当软总线建立与设备的authconn后就可进行认证,具体的方式是:
//从tcp端口读取数据包,并解析
void ProcessDataEvent(int fd)
{
//获取authconn
AuthConn *conn = FindAuthConnByFd(fd);
//申请buf
if (conn->db.buf == NULL) {
conn->db.buf = (char *)malloc(DEFAULT_BUF_SIZE);
(void)memset_s(conn->db.buf, DEFAULT_BUF_SIZE, 0, DEFAULT_BUF_SIZE);
conn->db.size = DEFAULT_BUF_SIZE;
conn->db.used = 0;
}
DataBuffer *db = &conn->db;
char *buf = db->buf;
int used = db->used;
int size = db->size;
//接收认证数据到buf
int rc = AuthConnRecv(fd, buf, used, size - used, 0);
used += rc;
//解析tcp包头,处理包内信息
int processed = ProcessPackets(conn, buf, size, used);
db->used = used;
}
ProcessPackets:根据module参数对软总线的数据分支处理
//解析tcp包头,处理包内信息
static int ProcessPackets(AuthConn *conn, const char *buf, int size, int used)
{
int processed = 0;
while (processed + PACKET_HEAD_SIZE < used) {
//将buf首部数据转换成packet
Packet *pkt = ParsePacketHead(buf, processed, used - processed, size);
//有效数据长度
int len = pkt->dataLen;
//处理了buf的首部数据
processed += PACKET_HEAD_SIZE;
//处理payload数据
OnDataReceived(conn, pkt, buf + processed);
processed += len;
free(pkt);
pkt = NULL;
}
return processed;
}
//分支处理对端发来的认证数据
static void OnDataReceived(AuthConn *conn, const Packet *pkt, const char *data)
{
//module == MODULE_AUTH_SDK
if ((pkt->module > MODULE_HICHAIN) && (pkt->module <= MODULE_AUTH_SDK)) {
//建立AuthSession,处理认证逻辑
AuthInterfaceOnDataReceived(conn, pkt->module, pkt->seq, data, pkt->dataLen);
return;
}
cJSON *msg = DecryptMessage(pkt->module, data, pkt->dataLen);
//建立连接或完成认证
OnModuleMessageReceived(conn, pkt->module, pkt->flags, pkt->seq, msg);
cJSON_Delete(msg);
msg = NULL;
}
AuthInterfaceOnDataReceived
完成设备认证,其过程是:对端设备发送数据给本地的安全子系统的设备认证模块,设备认证模块负责处理数据,判断是否通过认证。
这里数据传输的方式就是使用 就是session
module的定义
#define MODULE_NONE 0
#define MODULE_TRUST_ENGINE 1 //可信类型,直接进行数据传输
#define MODULE_HICHAIN 2
#define MODULE_AUTH_SDK 3 //加密数据类型
#define MODULE_HICHAIN_SYNC 4
#define MODULE_CONNECTION 5 //进行ip及设备认证
#define MODULE_SESSION 6
#define MODULE_SMART_COMM 7
#define MODULE_AUTH_CHANNEL 8
#define MODULE_AUTH_MSG 9
//建立认证session,处理认证逻辑
void AuthInterfaceOnDataReceived(const AuthConn *conn, int module, long long seqId, const char *data, int dataLen)
{
//创建authsession数组,authsession是一个会话,负责认证逻辑
if (AuthSessionMapInit() != 0) {
return;
}
//初始化指定auth session
AuthSession *auth = AuthGetAuthSessionBySeqId(seqId);
if (auth == NULL) {
//获取失败,将conn添加到g_authSessionMap
auth = AuthGetNewAuthSession(conn, seqId, g_authSessionId);
++g_authSessionId;
}
//对不同模组使用不同认证方式
switch (module) {
case MODULE_AUTH_SDK:
//使用hichain处理auth数据
AuthProcessReceivedData(auth->sessionId, data, dataLen);
break;
}
return;
}
//调用安全子系统的设备认证模块,来完成对端设备的数据的认证
static void AuthProcessReceivedData(uint32_t sessionId, const char *data, int dataLen)
{
if (g_hcHandle == NULL) {
//初始化hichain,hichain是设备认证模块要使用的对象
if (AuthInitHiChain(sessionId) != 0) {
AuthDelAuthSessionBySessionId(sessionId);
return;
}
}
struct uint8_buff request = {(uint8_t *)data, dataLen, dataLen};
//把数据传递给安全认证子系统,处理的结果会通过回调函数反馈
if (receive_data(g_hcHandle, &request) != HC_OK) {
return;
}
}
OnModuleMessageReceived
处理可信设备
//处理module消息
static void OnModuleMessageReceived(AuthConn *conn, int module, int flags, long long seq, const cJSON *msg)
{
switch (module) {
//可信类型,能直接进行数据传输
case MODULE_TRUST_ENGINE: {
if (((unsigned int)flags & FLAG_REPLY) == 0) {
//通过发送本地deviceid 使通道建立?
OnMsgOpenChannelReq(conn, seq, msg);
}
break;
}
//连接类型,需要进行ip及设备认证
case MODULE_CONNECTION: {
OnMessageReceived(conn, seq, msg);
break;
}
}
return;
}
//验证设备的ip或deviceid
void OnMessageReceived(AuthConn *conn, long long seq, const cJSON *msg)
{
cJSON *codeJson = cJSON_GetObjectItem(msg, "CODE");
int code = codeJson->valueint;
//回复对端
switch (code) {
case CODE_VERIIFY_IP: {
OnVerifyIp(conn, seq, msg);
break;
}
case CODE_VERIFY_DEVID: {
OnVerifyDeviceId(conn, seq, msg);
break;
}
}
}
问题待解决
hichain的认证过程
hichain的认证目前没有找到文档,只能通过源码,猜测以下内容:初始化hichain所需要的对象,软总线作为一个媒介,在hichain和对端设备之间传递数据。
//初始化hichain
static int AuthInitHiChain(uint32_t sessionId)
{
struct session_identity serverIdentity = {
sessionId,
{AUTH_DEFAULT_ID_LEN, AUTH_DEFAULT_ID},
{AUTH_DEFAULT_ID_LEN, AUTH_DEFAULT_ID},
0
};
//定义设备认证回调函数,会在安全子系统中得到执行
struct hc_call_back hiChainCallback = {
AuthOnTransmit, //发送hichain的数据
AuthGetProtocolParams, //获取会话id
AuthSetSessionKey, //保存hc_session_key
AuthSetServiceResult, //认证结果回调
AuthConfirmReceiveRequest
};
//获取hichain,HC_ACCESSORY表示本地是附属设备,收超级终端控制
g_hcHandle = get_instance(&serverIdentity, HC_ACCESSORY, &hiChainCallback);
return 0;
}
tcp server 连接过程
OnModuleMessageReceived()函数处理的是什么逻辑?