文章目录

  • 软总线
  • 一、会话传输
    • 1.1、CreateSessionServer
    • 1.2、SendBytes
  • 二、设备发现
    • 2.1、发布服务
      • 2.1.1、wifi事件
      • 2.1.2、coap服务器
    • 2.2、软总线
      • 2.2.1、SelectSessionLoop
      • 2.2.2、WaitProcess
      • 2.2.3、OnConnectEvent
      • 2.2.4、OnDataEvent
        • AuthInterfaceOnDataReceived
        • OnModuleMessageReceived
  • 问题待解决
    • hichain的认证过程
    • tcp server 连接过程

软总线

本文简单介绍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_sessionMgrif (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)
{//获取sessionTcpSession *session = GetSessionById(sessionfd);//打包数据到cipherBufchar *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;}//初始化modulePublishModule *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 backPublishCallback(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]; //上层应用packagenameint 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 连接上wifiif (para) {//获取ipchar 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 serverint 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报文的数据,封装成decodePacketCOAP_SoftBusDecode(&decodePacket, recvBuffer, nRead);//响应远程发现者PostServiceDiscover(&decodePacket);free(recvBuffer);
}

响应的内容是什么?具体来看PostServiceDiscover():

从对端发来的coap报文中,可解析出对端的ip地址和url,然后将本地设备信息和对端地址组成coap报文,发送给对端

//给远程设备发送响应
void PostServiceDiscover(const COAP_Packet *pkt)
{char *remoteUrl = NULL;//从pkt中,可获取对端设备的ip等信息,封装成deviceinfoDeviceInfo deviceInfo;(void)memset_s(&deviceInfo, sizeof(deviceInfo), 0, sizeof(deviceInfo));//解析pkt->payload.buffer数据到deviceInfoif (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;//初始化CoapRequestCoapRequest coapRequest;coapRequest.remoteUrl = remoteUrl;  //资源地址coapRequest.remoteIp = remoteIp;    //ip地址//payload包含了本地设备的信息char *payload = PrepareServiceDiscover();//以下就是构建coapRequestCOAP_ReadWriteBuffer sndPktBuff = {0};sndPktBuff.readWriteBuf = calloc(1, COAP_MAX_PDU_SIZE);sndPktBuff.size = COAP_MAX_PDU_SIZE;sndPktBuff.len = 0;//根据pkt和ip构建coap报文sndPktBuffret = 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 sessionTcpSession *session = CreateTcpSession();//把authConn的deivceid复制到sessionAuthConn* 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连接添加sessionsession->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对应的sessionfor (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连接请求,响应请求,执行回调函数onSessionOpenedbool 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_dataFdRefreshMaxFd(g_dataFd);//执行回调函数: 连接成功 OnConnectEventif (g_callback->onConnectEvent(g_dataFd, inet_ntoa(addrClient.sin_addr)) != 0) {CloseAuthSessionFd(g_dataFd);}}//执行回调函数:数据接收 OnDataEventif (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;}//还未认证,设置aconnaconn = calloc(1, sizeof(AuthConn));//复制ip、fd给aconnint 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)
{//获取authconnAuthConn *conn = FindAuthConnByFd(fd);//申请bufif (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;//接收认证数据到bufint 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首部数据转换成packetPacket *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_SDKif ((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 sessionAuthSession *auth = AuthGetAuthSessionBySeqId(seqId);if (auth == NULL) {//获取失败,将conn添加到g_authSessionMapauth = 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,  //获取会话idAuthSetSessionKey,      //保存hc_session_keyAuthSetServiceResult,   //认证结果回调AuthConfirmReceiveRequest};//获取hichain,HC_ACCESSORY表示本地是附属设备,收超级终端控制g_hcHandle = get_instance(&serverIdentity, HC_ACCESSORY, &hiChainCallback);return 0;
}

tcp server 连接过程

OnModuleMessageReceived()函数处理的是什么逻辑?

OpenHarmony 软总线lite 源码分析相关推荐

  1. [Vue源码分析]自定义事件原理及事件总线的实现

    最近小组有个关于vue源码分析的分享会,提前准备一下- 前言: 我们都知道Vue中父组件可以通过 props 向下传数据给子组件:子组件可以通过向$emit触发一个事件,在父组件中执行回调函数,从而实 ...

  2. FFmpeg简述,源码分析,录制/压缩/水印/剪切/旋转/滤镜/美颜/上传视频等(CPU软编码和解码)

    > ffmpeg源码分析 ffmpeg源码简析(一)结构总览- https://blog.csdn.net/Louis_815/article/details/79621056 FFmpeg的库 ...

  3. v67.03 鸿蒙内核源码分析(字符设备) | 绝大多数设备都是这类 | 百篇博客分析OpenHarmony源码

    曾子曰:"君子以文会友,以友辅仁." <论语>:颜渊篇 百篇博客系列篇.本篇为: v67.xx 鸿蒙内核源码分析(字符设备篇) | 绝大多数设备都是这类 文件系统相关篇 ...

  4. v62.02 鸿蒙内核源码分析(文件概念) | 为什么说一切皆是文件 | 百篇博客分析OpenHarmony源码

    司马牛忧曰:"人皆有兄弟,我独亡."子夏曰:"商闻之矣:死生有命,富贵在天.君子敬而无失,与人恭而有礼.四海之内,皆兄弟也.君子何患乎无兄弟也?" <论语 ...

  5. v79.01 鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上) | 百篇博客分析OpenHarmony源码

    百篇博客分析|本篇为:(用户态锁篇) | 如何使用快锁Futex(上) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...

  6. v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码

    Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https ...

  7. v65.05 鸿蒙内核源码分析(挂载目录) | 为何文件系统需要挂载 | 百篇博客分析OpenHarmony源码

    子曰:"君子成人之美,不成人之恶.小人反是." <论语>:颜渊篇 百篇博客系列篇.本篇为: v65.xx 鸿蒙内核源码分析(挂载目录篇) | 为何文件系统需要挂载 文件 ...

  8. v69.04 鸿蒙内核源码分析(文件句柄) | 你为什么叫句柄 | 百篇博客分析OpenHarmony源码

    子曰:"诵诗三百,授之以政,不达:使于四方,不能专对:虽多,亦奚以为?" <论语>:子路篇 百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | ...

  9. v70.05 鸿蒙内核源码分析(管道文件) | 如何降低数据流动成本 | 百篇博客分析OpenHarmony源码

    子曰:"其身正,不令而行:其身不正,虽令不从." <论语>:子路篇 百篇博客系列篇.本篇为: v70.xx 鸿蒙内核源码分析(管道文件篇) | 如何降低数据流动成本 文 ...

最新文章

  1. Floodlight 处理交换机增加/移除过程
  2. 设计模式之-观察者模式(Observer Design Pattern)
  3. 推土机:将JAXB对象映射到业务/域对象
  4. 新娘JAVA_Java之趣味编程结婚问题
  5. java自带的jvm在哪里看_使用jdk工具查看jvm笔记
  6. 大数据之Oozie——源码分析(一)程序入口
  7. 数据挖掘第三版课后习题
  8. 关于Linux内核学习
  9. linux u盘 写保护,高手分享U盘被写保护的解决方案
  10. 【PBR理论】第2讲 BRDF理想漫反射(Diffuse)
  11. NOD32升级ID获取器For流星无语
  12. 马科维茨模型的实例验证与思考(含Python代码)
  13. r4900g3系统安装linux_H3C UniServer R4900 G3服务器NVMe硬盘配置阵列方法以及相关操作...
  14. 字节跳动算法工程师总结:成功入职阿里月薪45K
  15. 恢复出厂设置android手机号码,手机怎么恢复出厂设置 安卓手机恢复出厂设置方法汇总...
  16. DIV布局之道一:DIV块的水平并排、垂直并排
  17. win10下搭建grpc 以及demo(idea maven java)
  18. 坚果云和百度云的对比
  19. Windows平板真机调试
  20. BACKUP DATABASE

热门文章

  1. Windows 10 缺少 efi 分区无法启动的修复
  2. 微软WIN7系统瘦身全攻略
  3. windows进程管理器_探究 Process Explorer 进程树选项灰色问题
  4. python列表替换元素_24_Pandas.DataFrame,Series元素值的替换(replace)
  5. 点乘/内积/数量积;叉乘/向量积;矩阵乘法;哈达马积;克罗内克积;卷积
  6. Java学习之JavaFX安装
  7. scscanner:一款功能强大的大规模状态码扫描工具
  8. 华东师范大学计算机学院和软件学院,华东师大撤销计算机科学与软件工程等学院建制,成立信息学部...
  9. IOS模拟器怎么安装应用程序
  10. 打印机有重影问题解决方案