天天看点

OpenHarmony 软总线lite 源码分析

文章目录

  • ​​软总线​​
  • ​​一、会话传输​​
  • ​​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_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 连接过程

继续阅读