OpenHarmony分布式软总线流程分析v1.0丨2.开启软总线,建立连接 原创 精华

丨张明亮丨
发布于 2021-6-26 08:25
浏览
21收藏

更新时间:2021年6月26日 @亮子力

这次的软总线流程分析字数太多,上传了好几次都失败了。所以我分成了2个部分。
1.被发现端,发布服务
2.开启软总线,建立连接

完整版可以下载文末的pdf。

@toc

二、当接入网络,触发WifiEventTrigger(),开启软总线

Z:\harmony110\foundation\communication\softbus_lite\discovery\discovery_service\source\discovery_service.c
void WifiEventTrigger(unsigned int para)
{
    DeviceInfo *localDev = GetCommonDeviceInfo();
    if (localDev == NULL) {
        return;
    }

    int ret;
    if (para) {
        char wifiIp[MAX_DEV_IP_LEN] = {0};
        CoapGetIp(wifiIp, MAX_DEV_IP_LEN, 0); //参考1.获取本设备ip
        if (strcmp(wifiIp, "0.0.0.0") == 0) {
            SOFTBUS_PRINT("[DISCOVERY] WifiEventTrigger new event interupt.\n");
            return;
        }
        ret = memcpy_s(localDev->deviceIp, sizeof(localDev->deviceIp), wifiIp, sizeof(wifiIp));
    } else {
        ret = memset_s(localDev->deviceIp, sizeof(localDev->deviceIp), 0, sizeof(localDev->deviceIp));
    }

    if (ret != ERROR_SUCCESS) {
        return;
    }

    if (BusManager(para) != ERROR_SUCCESS) { //参考2.BusManager()启动软总线
        SOFTBUS_PRINT("[DISCOVERY] WifiEventTrigger StartBusManager(%d) fail\n", para);
        return;
    }

    if (CoapRegisterDeviceInfo() != ERROR_SUCCESS) {
        SOFTBUS_PRINT("[DISCOVERY] WifiEventTrigger CoapRegisterDeviceInfo fail\n");
        return;
    }

    if (DoRegistService(COAP) != ERROR_SUCCESS) {
        SOFTBUS_PRINT("[DISCOVERY] WifiEventTrigger DoRegistService fail\n");
        return;
    }
}

1.获取本设备ip

Z:\harmony110\foundation\communication\softbus_lite\discovery\coap\source\coap_discover.c
//通过CoapGetIp()循环获取本地设备wifi连接后的IP地址,并放入到deviceInfo->deviceIp中。后续会使用。
void CoapGetIp(char *ip, int length, int finite)
{
    if (ip == NULL || length != NSTACKX_MAX_IP_STRING_LEN) {
        return;
    }

    g_queryIpFlag = 1;
    int count = finite ? GET_IP_TIMES : GET_IP_INFINITE;
    while (g_queryIpFlag) {
        CoapGetWifiIp(ip, length);//获取ip方式根据内核不同,会有多个方式
        if (CheckIpIsValid(ip, strlen(ip)) == 0) {
            break;
        }

        if (count == 0) {
            break;
        }
        count--;
        usleep(TEN_MS);	//#define TEN_MS  (10 * 1000)//usleep 10ms,每次获取IP间隔10ms
    }
    return;
}

2.BusManager()启动软总线


Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\bus_manager.c
int BusManager(unsigned int startFlag)
{
    if (startFlag == 1) {
        return StartBus();
    } else {
        return StopBus();
    }
}
==============================================================================
int StartBus(void)
{
    if (g_busStartFlag == 1) {
        return 0;
    }
    DeviceInfo *info = GetCommonDeviceInfo();
    if (info == NULL) {
        return ERROR_FAIL;
    }

    g_baseLister.onConnectEvent = OnConnectEvent;	//当存在新连接时调用此函数
    g_baseLister.onDataEvent = OnDataEvent;			//当存在新数据时调用此函数
    //StartListener()函数负责为认证模块提供通道完成初始化
    int authPort = StartListener(&g_baseLister, info->deviceIp);  //参考:2.1.启动侦听
    if (authPort < 0) {
        SOFTBUS_PRINT("[AUTH] StartBus StartListener fail\n");
        return ERROR_FAIL;
    }
    info->devicePort = authPort;
	//StartSession()函数负责初始化业务的session管理
    int sessionPort = StartSession(info->deviceIp);	//参考:2.2.启动会话
    if (sessionPort < 0) {
        SOFTBUS_PRINT("[AUTH] StartBus StartSession fail\n");
        StopListener();
        return ERROR_FAIL;
    }

    AuthMngInit(authPort, sessionPort);
    g_busStartFlag = 1;

    SOFTBUS_PRINT("[AUTH] StartBus ok\n");
    return 0;
}
==============================================================================
//回调函数的处理
//trans_service模块的使用者设置的回调函数将在存在新连接、和新数据时被调用
//比如认证模块通过以下函数完成认证动作:OnConnectEvent()函数中完成对新连接的处理, OnDataEvent()函数中完成对新数据的处理。

int OnConnectEvent(int fd, const char *ip)
    ProcessConnectEvent(fd, ip);
    return 0;
}

int OnDataEvent(int fd)
    ProcessDataEvent(fd);
    return 0;
}

2.1.启动监听,StartListener(),监听与发现端建立连接

StartListener()函数的底层存在对应不同版本平台的适配函数,这印证了鸿蒙OS各部分解耦的模块化设计思想,针对不同的硬件设备,组合成最适合该设备的OS。比如创建线程时采用了统一的static void WaitProcess(void)函数,而其内部封装了不同底层API的适配代码。

Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\libdistbus\auth_conn_manager.c
//StartListener()函数负责为认证模块提供通道完成初始化
//该函数主要是创建了一个WaitProcess 的线程,该线程用于对g_maxFd 所代表的文件描述符利用select 函数进行监控,若返回值大于0,则调用ProcessAuthData 函数。该函数完成对建立的链接的数据进行收发,并对收到的数据进行处理的工作,两个处理事件的函数分别是onConnectEvent和onDataEvent,即前面注册的两个回调函数。其中onConnectEvent 函数主要是为新建立连接的设备创建AuthConnNode 类型的节点,并将其加入链表中;onDataEvent 函数是对AuthConnNode节点中的数据成员进行内存分配及对数据进行处理。
#if defined(__LITEOS_M__) || defined(__LITEOS_RISCV__)
int StartListener(BaseListener *callback, const char *ip)	// *callback,回调函数
{
    if (callback == NULL || ip == NULL) {
        return -DBE_BAD_PARAM;
    }

    g_callback = callback;
	//InitListenFd()函数完成监听TCP socket的创建和监听,其中IP地址和端口号由上层调用者指定。
    int rc = InitListenFd(ip, SESSIONPORT);	//
    if (rc != DBE_SUCCESS) {
        return -DBE_BAD_PARAM;
    }

    osThreadAttr_t attr;
    attr.name = "trans_auth_task";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    attr.priority = osPriorityNormal5; // LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO -> cmsis prio
	//osThreadNew()在不同平台上会有不同的实现,在LITEOS_A和Linux平台上, osThreadNew()会调用兼容POSIX的pthread_create()完成线程的创建
    g_uwTskLoID = osThreadNew((osThreadFunc_t)WaitProcess, NULL, &attr);//监听新连接和数据WaitProcess(void)
    if (NULL == g_uwTskLoID) {
        SOFTBUS_PRINT("[TRANS] StartListener task create fail\n");
        return -1;
    }

    SOFTBUS_PRINT("[TRANS] StartListener ok\n");
    return GetSockPort(g_listenFd);
}
==============================================================================
static void WaitProcess(void)//监听新连接和数据WaitProcess(void)
{
    SOFTBUS_PRINT("[TRANS] WaitProcess begin\n");
    fd_set readSet;
    fd_set exceptfds;

    while (1) {//WaitProcess()使用忙等方式,调用select()来监听listenFd和数据g_dataFd的信息,如果监听到有数据可读,则进入ProcessAuthData来处理。
        FD_ZERO(&readSet);
        FD_ZERO(&exceptfds);
        FD_SET(g_listenFd, &readSet);
        if (g_dataFd >= 0) {
            FD_SET(g_dataFd, &readSet);
            FD_SET(g_dataFd, &exceptfds);
        }
        int ret = select(g_maxFd + 1, &readSet, NULL, &exceptfds, NULL);//监听g_listenFd和数据g_dataFd的信息
        if (ret > 0) {//如果监听到有数据可读,则进入ProcessAuthData来处理。
            if (!ProcessAuthData(g_listenFd, &readSet)) {//【重要的监听】参考:
                SOFTBUS_PRINT("[TRANS] WaitProcess ProcessAuthData fail\n");
                StopListener();
                break;
            }
        } else if (ret < 0) {//如果发现g_dataFd有异常信息,则将其关闭。其中g_dataFd是由listenFd监听到连接时创建的socket。
            if (errno == EINTR || (g_dataFd > 0 && FD_ISSET(g_dataFd, &exceptfds))) {
                SOFTBUS_PRINT("[TRANS] errno == EINTR or g_dataFd is in exceptfds set.\n");
                CloseAuthSessionFd(g_dataFd);
                continue;
            }
            SOFTBUS_PRINT("[TRANS] WaitProcess select fail, stop listener\n");
            StopListener();
            break;
        }
    }
}

2.2.启动会话,StartSession()

//StartSession()函数负责初始化业务的session管理
//StartSession 该函数只有一个参数,即const char *ip,也就是一个IP,和StartListener函数中的IP 是一样的。该函数是为全局变量g_sessionMgr 申请空间及初始化,然后根据所给的参数创建socket 文件描述符并监听,之后通过调用StartSelectLoop 函数创建SelectSessionLoop 的线程,该线程将socket 文件描述符加入集合,并调用select 函数进行监控,若函数的返回值大于0,则调用ProcessData 函数,该函数有两个分支,若socket 未创建session则为其创建session;若已创建session,则处理其数据部分
Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\bus_manager.c
int StartSession(const char *ip)
{
    int port = CreateTcpSessionMgr(true, ip);
    return port;
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\libdistbus\tcp_session_manager.c
int CreateTcpSessionMgr(bool asServer, const char* localIp)
{
    if (localIp == NULL) {
        return TRANS_FAILED;
    }

    if (InitTcpMgrLock() != 0 || GetTcpMgrLock() != 0) {
        return TRANS_FAILED;
    }

    int ret = InitGSessionMgr();
    if (ReleaseTcpMgrLock() != 0 || ret != 0) {
        FreeSessionMgr();
        return TRANS_FAILED;
    }
    g_sessionMgr->asServer = asServer;//该函数是为全局变量g_sessionMgr 申请空间及初始化
    int listenFd = OpenTcpServer(localIp, DEFAULT_TRANS_PORT);
    if (listenFd < 0) {
        SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr OpenTcpServer fail\n");
        FreeSessionMgr();
        return TRANS_FAILED;
    }
    int rc = listen(listenFd, LISTEN_BACKLOG);//然后根据所给的参数创建socket 文件描述符并监听
    if (rc != 0) {
        SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr listen fail\n");
        CloseSession(listenFd);
        FreeSessionMgr();
        return TRANS_FAILED;
    }
    g_sessionMgr->listenFd = listenFd;

    signal(SIGPIPE, SIG_IGN);
    if (StartSelectLoop(g_sessionMgr) != 0) {//之后通过调用StartSelectLoop 函数创建SelectSessionLoop 的线程
        SOFTBUS_PRINT("[TRANS] CreateTcpSessionMgr StartSelectLoop fail\n");
        CloseSession(listenFd);
        FreeSessionMgr();
        return TRANS_FAILED;
    }
    return GetSockPort(listenFd);
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\libdistbus\tcp_session_manager.c
#if defined(__LITEOS_M__) || defined(__LITEOS_RISCV__)
int StartSelectLoop(TcpSessionMgr *tsm)
{
    if (tsm == NULL) {
        return TRANS_FAILED;
    }

    if (tsm->isSelectLoopRunning) {
        return 0;
    }

    osThreadId_t sessionLoopTaskId;

    osThreadAttr_t attr;
    attr.name = "trans_session_task";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    attr.priority = osPriorityNormal5; // LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO -> cmsis prio
	//该线程将socket 文件描述符加入集合,并调用select 函数进行监控,
    sessionLoopTaskId = osThreadNew((osThreadFunc_t)SelectSessionLoop, (void *)tsm, &attr);
    if (NULL == sessionLoopTaskId) {
        SOFTBUS_PRINT("[TRANS] StartSelectLoop TaskCreate fail\n");
        return TRANS_FAILED;
    }

    return 0;
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\libdistbus\tcp_session_manager.c
static void SelectSessionLoop(TcpSessionMgr *tsm)
{
    if (tsm == NULL) {
        return;
    }
    SOFTBUS_PRINT("[TRANS] SelectSessionLoop begin\n");
    tsm->isSelectLoopRunning = true;
    while (true) {
        fd_set readfds;
        fd_set exceptfds;
        int maxFd = InitSelectList(tsm, &readfds, &exceptfds);
        if (maxFd < 0) {
            break;
        }
		
        errno = 0;
        int ret = select(maxFd + 1, &readfds, NULL, &exceptfds, NULL);
        if (ret < 0) {
            SOFTBUS_PRINT("RemoveExceptSessionFd\r\n");
            if (errno == EINTR || RemoveExceptSessionFd(tsm, &exceptfds) == 0) {
                continue;
            }
            SOFTBUS_PRINT("[TRANS] SelectSessionLoop close all Session\n");
            CloseAllSession(tsm);
            break;
        } else if (ret == 0) {
            continue;
        } else {//若函数的返回值大于0,则调用ProcessData 函数,
            ProcessData(tsm, &readfds);
        }
    }
    tsm->isSelectLoopRunning = false;
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\libdistbus\tcp_session_manager.c
static void ProcessData(TcpSessionMgr *tsm, fd_set *rfds)
{
    if (tsm == NULL || tsm->listenFd == -1) {
        return;
    }
	//该函数有两个分支,若socket 未创建session则为其创建session;若已创建session,则处理其数据部分
    if (FD_ISSET(tsm->listenFd, rfds)) {
        ProcessConnection(tsm);
        return;
    }

    ProcessSesssionData(tsm, rfds);
}


总结:

当软总线启动后,就会开始监听新连接,OnConnectEvent()函数中完成对新连接的处理, OnDataEvent()函数中完成对新数据的处理。

三、当发现端(比如手机)发送广播

当鸿蒙手机开启锁屏后,会在局域网内发送UDP广播,当开发板(服务端)检测到广播会调用下面的函数。

1.当检测到局域网内UDP广播包,HandleReadEvent()

Z:\harmony110\foundation\communication\softbus_lite\discovery\coap\source\coap_discover.c
static void HandleReadEvent(int fd)
{
    int socketFd = fd;
    unsigned char *recvBuffer = calloc(1, COAP_MAX_PDU_SIZE + 1);
    if (recvBuffer == NULL) {
        return;
    }
    ssize_t nRead;
    nRead = CoapSocketRecv(socketFd, recvBuffer, COAP_MAX_PDU_SIZE);//这个函数读取socket的内容
    if ((nRead == 0) || (nRead < 0 && errno != EAGAIN &&
        errno != EWOULDBLOCK && errno != EINTR)) {
        free(recvBuffer);
        return;
    }

    COAP_Packet decodePacket;
    (void)memset_s(&decodePacket, sizeof(COAP_Packet), 0, sizeof(COAP_Packet));
    decodePacket.protocol = COAP_UDP;
    COAP_SoftBusDecode(&decodePacket, recvBuffer, nRead);//然后调用COAP_SoftBusDecode来解码
    PostServiceDiscover(&decodePacket);//解码后的报文由PostServiceDiscover来处理,参考下面解析
    free(recvBuffer);
}
==============================================================================
void PostServiceDiscover(const COAP_Packet *pkt)
{
    char *remoteUrl = NULL;
    DeviceInfo deviceInfo;

    if (pkt == NULL) {
        return;
    }

    (void)memset_s(&deviceInfo, sizeof(deviceInfo), 0, sizeof(deviceInfo));
    //关于GetServiceDiscoverInfo()这个解包函数暂时不深挖了,TODO
    if (GetServiceDiscoverInfo(pkt->payload.buffer, pkt->payload.len, &deviceInfo, &remoteUrl) != NSTACKX_EOK) {
        return;
    }

    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));
printf(">>>>>%s:%d\nremoteUrl:%s\nwifiIpAddr:%s\n",__FILE__,__LINE__,remoteUrl,wifiIpAddr);//添加打印调试
    if (remoteUrl != NULL) {
        CoapResponseService(pkt, remoteUrl, wifiIpAddr);//通过解析到的手机(发现端)的地址,应答服务
        free(remoteUrl);
    }
}
==============================================================================
static int CoapResponseService(const COAP_Packet *pkt, const char* remoteUrl, const char* remoteIp)
{
    int ret;
    CoapRequest coapRequest;
    (void)memset_s(&coapRequest, sizeof(coapRequest), 0, sizeof(coapRequest));
    coapRequest.remoteUrl = remoteUrl;
    coapRequest.remoteIp = remoteIp;
    char *payload = PrepareServiceDiscover();
    if (payload == NULL) {
        return NSTACKX_EFAILED;
    }
printf(">>>>>%s:%d\npayload:%s\n",__FILE__,__LINE__,payload);//添加打印调试
    COAP_ReadWriteBuffer sndPktBuff = {0};
    sndPktBuff.readWriteBuf = calloc(1, COAP_MAX_PDU_SIZE);
    if (sndPktBuff.readWriteBuf == NULL) {
        free(payload);
        return NSTACKX_EFAILED;
    }
    sndPktBuff.size = COAP_MAX_PDU_SIZE;
    sndPktBuff.len = 0;

    ret = BuildSendPkt(pkt, remoteIp, payload, &sndPktBuff);//构建要发回的包
    free(payload);
    if (ret != DISCOVERY_ERR_SUCCESS) {
        free(sndPktBuff.readWriteBuf);
        sndPktBuff.readWriteBuf = NULL;
        return ret;
    }
    coapRequest.data = sndPktBuff.readWriteBuf;
    coapRequest.dataLength = sndPktBuff.len;
    ret = CoapSendRequest(&coapRequest);	//发送
    free(sndPktBuff.readWriteBuf);
    sndPktBuff.readWriteBuf = NULL;

    return ret;
}

2.当检测到新连接,ProcessAuthData()

Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\libdistbus\auth_conn_manager.c:85
//无论是新连接请求,还是已有连接中有数据到来,均会进入本函数。
static bool ProcessAuthData(int listenFd, const fd_set *readSet)
{
    if (readSet == NULL || g_callback == NULL || g_callback->onConnectEvent == NULL ||
        g_callback->onDataEvent == NULL) {
        return false;
    }

    if (FD_ISSET(listenFd, readSet)) {//判断是否是listenFd上存在消息
        struct sockaddr_in addrClient = {0};
        socklen_t addrLen = sizeof(addrClient);
		//如果是,则说明当前存在新的连接,这时调用accept()完成链接创建,新创建的socket的fd被存储在g_dataFd中,
        g_dataFd = accept(listenFd, (struct sockaddr *)(&addrClient), &addrLen);
        if (g_dataFd < 0) {
            CloseAuthSessionFd(listenFd);
            return false;
        }
        RefreshMaxFd(g_dataFd);
        //同时调用g_callback->onConnectEvent通知认证模块有新的连接事件发生,并将新创建的fd和client的IP地址告知认证模块。
        //与此同时,创建g_dataFd时候需要刷新g_maxFd,以保证在WaitProcess()中的下一次select()操作时中,会监听到g_dataFd上的事件。
        if (g_callback->onConnectEvent(g_dataFd, inet_ntoa(addrClient.sin_addr)) != 0) {	//参考下面解析OnConnectEvent()
            CloseAuthSessionFd(g_dataFd);
        }
    }
	//如果FD_ISSET()判断出g_dataFd上存在消息,则说明已完成握手的连接向本节点发送了数据,
    if (g_dataFd > 0 && FD_ISSET(g_dataFd, readSet)) {	//参考下面解析OnDataEvent()
        g_callback->onDataEvent(g_dataFd);//这时函数回调g_callback->onDataEvent(),把控制权返回给调用者,以处理接收到的数据。
    }

    return true;
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\bus_manager.c
int OnConnectEvent(int fd, const char *ip)
{
    ProcessConnectEvent(fd, ip);	//2.1当建立新连接
    return 0;
}
int OnDataEvent(int fd)
{
    ProcessDataEvent(fd);			//2.2当接收到新数据
    return 0;
}

2.1当建立新连接,OnConnectEvent()

Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\wifi_auth_manager.c:192行
void ProcessConnectEvent(int fd, const char *ip)
{
    SOFTBUS_PRINT("[AUTH] ProcessConnectEvent fd = %d\n", fd);
    AuthConn *aconn = FindAuthConnByFd(fd); //通过fd查找验证连接

    aconn = calloc(1, sizeof(AuthConn));  //? 系统声明吗?

    int ret = strcpy_s(aconn->deviceIp, sizeof(aconn->deviceIp), ip);//字符串复制函数

    aconn->fd = fd;

    ret = AddAuthConnToList(aconn);//添加认证连接到列表
}
Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\wifi_auth_manager.c:554
这个函数被调用3次??
数据事件进程
void ProcessDataEvent(int fd)
{
    SOFTBUS_PRINT("[AUTH] ProcessDataEvent fd = %d\n", fd);
    AuthConn *conn = FindAuthConnByFd(fd); //通过fd查找验证连接

    if (conn->db.buf == NULL) {
        conn->db.buf = (char *)malloc(DEFAULT_BUF_SIZE);
        if (conn->db.buf == NULL) {
            return;
        }
        (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;

    int rc = AuthConnRecv(fd, buf, used, size - used, 0);
    if (rc == 0) {
        return;
    } else if (rc < 0) {
        CloseConn(conn);
        return;
    }

    used += rc;
    int processed = ProcessPackets(conn, buf, size, used); //参考下面解析1
    if (processed > 0) {
        used -= processed;
        if (used != 0) {
            if (memmove_s(buf, processed, buf, used) != EOK) {
                CloseConn(conn);
                return;
            }
        }
    } else if (processed < 0) {
        CloseConn(conn);
        return;
    }

    db->used = used;
    SOFTBUS_PRINT("[AUTH] ProcessDataEvent ok\n");
}

1.\Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\wifi_auth_manager.c:527行

static int ProcessPackets(AuthConn *conn, const char *buf, int size, int used)
{
    int processed = 0;
    while (processed + PACKET_HEAD_SIZE < used) {
        Packet *pkt = ParsePacketHead(buf, processed, used - processed, size);//参考下面解析1

        int len = pkt->dataLen;//将解析完包的大小,赋值给len
        processed += PACKET_HEAD_SIZE;//+24
        OnDataReceived(conn, pkt, buf + processed);//参考下面解析2
        processed += len;
        free(pkt);
        pkt = NULL;
    }
    return processed;
}

1.\Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\wifi_auth_manager.c:485行

//解析包头,这个函数返回一个packet的结构体
static Packet *ParsePacketHead(const char *buf, int offset, int len, int size)
{
    unsigned int identifier = GetIntFromBuf(buf, offset); //1.从buf获取识别码
    
    offset += DEFAULT_INT_LEN; //将偏移量+4
    int module = GetIntFromBuf(buf, offset);//2.从buf获取module
    
    offset += DEFAULT_INT_LEN;//将偏移量+4
    long long seq = 0;
    if (GetLongFromBuf(buf, offset, &seq) != 0) {//3.从buf获取序列号
        return NULL;
    }
    
    offset += DEFAULT_LONG_LEN;//将偏移量+4
    int flags = GetIntFromBuf(buf, offset);//4.从buf获取标志位
    
    offset += DEFAULT_INT_LEN;//将偏移量+4
    int dataLen = GetIntFromBuf(buf, offset);//5.从buf获取数据长度

    SOFTBUS_PRINT("[AUTH] ParsePacketHead module=%d, seq=%lld, flags=%d, datalen=%d\n", module, seq, flags, dataLen);

    Packet *packet = (Packet *)malloc(sizeof(Packet));
    packet->module = module;
    packet->seq = seq;
    packet->flags = flags;
    packet->dataLen = dataLen;

    return packet;
}

2.\Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\wifi_auth_manager.c:446行

static void OnDataReceived(AuthConn *conn, const Packet *pkt, const char *data)
{
    SOFTBUS_PRINT("[AUTH] OnDataReceived\n"); //数据接收
    if ((pkt->module > MODULE_HICHAIN) && (pkt->module <= MODULE_AUTH_SDK)) {
        //接收数据的认证接口函数,参考解析1
        AuthInterfaceOnDataReceived(conn, pkt->module, pkt->seq, data, pkt->dataLen);
        return;
    }

    cJSON *msg = DecryptMessage(pkt->module, data, pkt->dataLen);
    if (msg == NULL) {
        SOFTBUS_PRINT("[AUTH] OnDataReceived DecryptMessage fail\n");
        return;
    }

    OnModuleMessageReceived(conn, pkt->module, pkt->flags, pkt->seq, msg);
    cJSON_Delete(msg);
    msg = NULL;
}
Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\auth_interface.c
void AuthInterfaceOnDataReceived(const AuthConn *conn, int module, long long seqId, const char *data, int dataLen)
{
    SOFTBUS_PRINT("[AUTH] AuthInterfaceOnDataReceived begin\n");

    if (AuthSessionMapInit() != 0) { //认证会话初始化
        return;
    }
    AuthSession *auth = AuthGetAuthSessionBySeqId(seqId);//身份验证-通过序列ID获取验证会话
    if (auth == NULL) {
        auth = AuthGetNewAuthSession(conn, seqId, g_authSessionId);//身份验证-获取新的验证会话
        if (auth == NULL) {
            return;
        }
        ++g_authSessionId;
    }

    switch (module) {
        case MODULE_AUTH_SDK:
            AuthProcessReceivedData(auth->sessionId, data, dataLen);//身份验证线程接收数据
            break;
        default:
            break;
    }
    return;
}

2.2当接收到新数据,OnDataEvent()

Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\wifi_auth_manager.c
void ProcessDataEvent(int fd)
{
    SOFTBUS_PRINT("[AUTH] ProcessDataEvent fd = %d\n", fd);
    AuthConn *conn = FindAuthConnByFd(fd);
    if (conn == NULL) {
        SOFTBUS_PRINT("ProcessDataEvent get authConn fail\n");
        return;
    }

    if (conn->db.buf == NULL) {
        conn->db.buf = (char *)malloc(DEFAULT_BUF_SIZE);
        if (conn->db.buf == NULL) {
            return;
        }
        (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;
	printf(">>>>>%s:%d\nbuf:%s\n",__FILE__,__LINE__,buf);
    int rc = AuthConnRecv(fd, buf, used, size - used, 0);//认证连接的返回信息
    if (rc == 0) {
        return;
    } else if (rc < 0) {
        CloseConn(conn);
        return;
    }
    used += rc;
    int processed = ProcessPackets(conn, buf, size, used);//对收到的包处理
    if (processed > 0) {
        used -= processed;
        if (used != 0) {
            if (memmove_s(buf, processed, buf, used) != EOK) {
                CloseConn(conn);
                return;
            }
        }
    } else if (processed < 0) {
        CloseConn(conn);
        return;
    }
    db->used = used;
    SOFTBUS_PRINT("[AUTH] ProcessDataEvent ok\n");
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\authmanager\source\auth_conn.c
int AuthConnRecv(int fd, char *buf, int offset, int count, int timeout)
{
    if ((buf == NULL) || (offset < 0) || (count <= 0) || (offset + count <= 0)) {
        return -1;
    }

    return TcpRecvData(fd, buf + offset, count, timeout);
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\utils\tcp_socket.c
int TcpRecvData(int fd, char *buf, int len, int timeout)
{
    return TcpRecvMessages(fd, buf, len, timeout, 0);
}
==============================================================================
Z:\harmony110\foundation\communication\softbus_lite\trans_service\source\utils\tcp_socket.c
static int32_t TcpRecvMessages(int fd, char *buf, uint32_t len, int timeout, int flags)
{printf(">>>>>%s:%d:%s()\n",__FILE__,__LINE__,__FUNCTION__);
    if (fd < 0 || buf == NULL || len == 0 || timeout < 0) {
        return -1;
    }
printf(">>>>>%s:%d\nfd:%d,len:%d,flags:%d\nTcpRecvMessages:buf:%s\n",__FILE__,__LINE__,fd,len,flags,buf);
    errno = 0;
    int32_t rc = recv(fd, buf, len, flags);
printf(">>>>>%s:%d\nfd:%d,len:%d,flags:%d,rc:%d\nTcpRecvMessages:buf:%s\n",__FILE__,__LINE__,fd,len,flags,rc,buf);
    if ((rc == -1) && (errno == EAGAIN)) {
        rc = 0;
    } else if (rc <= 0) {
        rc = -1;//rc = -1; //这里rc = -1,程序在这里跳出了,但是却找不到int32_t rc = recv(fd, buf, len, flags);的来源
    }
    return rc;
}
//通过添加2条打印信息发现,经过recv(fd, buf, len, flags)之后,buf有了数据,这数据来自手机端,并加密了。关于recv()打开下面文件
//Z:\harmony110\device\hisilicon\hispark_pegasus\sdk_liteos\third_party\lwip_sack\include\lwip\sockets.h:1589
//在函数的描述里可以看做,这里的recv()是作为API提供的,所以只能挖到这里了。

auth.设备认证机制

authmanager【提供设备认证机制和设备知识库管理】

当发现有请求时,调用ProcessDataEvent函数,收包,检验包头,根据数据包的类型确定不同的处理方式。类型主要包括以下三种:
MODULE_AUTH_SDK     加密数据类型
MODULE_TRUST_ENGINE 可信类型,直接进行数据传输
MODULE_CONNECTION  进行ip及设备认证

├── BUILD.gn
├── include
│   ├── auth_conn.h
│   ├── auth_interface.h
│   ├── bus_manager.h
│   ├── msg_get_deviceid.h
│   └── wifi_auth_manager.h
└── source
    ├── auth_conn.c【提供发送、接收、认证、获取秘钥功能】
    ├── auth_interface.c【管理各个会话节点、各个链接节点、各个秘钥节点,提供包括增删改查等功能】
    ├── bus_manager.c【主要通过deviceIp创建两个不同的listen,主要用来监听系统上有哪些device及新的device节点的创建;其中有两个回调函数OnConnectEvent和OnDataEvent,分别是用来处理设备节点的基本操作及节点数据的处理】
    ├── msg_get_deviceid.c【提供以cJSON格式获取各个设备的信息,包括设备id、链接信息、设备名、设备类型等】
    └── wifi_auth_manager.c【主要实现了连接管理和数据接收功能。连接管理包括连接的建立、断开及连接的查找。数据接收包括数据获取、包头及包长等的校验,并且为了简化包头数据读取,单独实现了对一个int型和一个long型数据的接收函数】

reference:

communication_dsoftbus: DSoftBus capabilities, including discovery, networking, and transmission | 软总线发现、组网、传输功能实现 (gitee.com)

https://www.cnblogs.com/hatsune/p/14328148.html

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
OpenHarmony分布式软总线流程分析v1.0.pdf 634.37K 953次下载
已于2021-6-27 19:42:20修改
14
收藏 21
回复
举报
13条回复
按时间正序
/
按时间倒序
Anzia
Anzia

膜拜研究底层的大佬。看了一下代码,好像是C代码比较多,C++的输出符比较少。所以openharmony底层是C吗🤔

回复
2021-6-26 09:04:40
丨张明亮丨
丨张明亮丨 回复了 Anzia
膜拜研究底层的大佬。看了一下代码,好像是C代码比较多,C++的输出符比较少。所以openharmony底层是C吗🤔

嗯 基本都是C实现的😄

回复
2021-6-26 09:57:14
liangkz_梁开祝
liangkz_梁开祝

我的菜~

 

另,请教一下,有没有让华为手机直接与Hi3516/Hi3861开发板进行交互的办法?

 

我本地的Hi3516/Hi3861开发板,publish服务之后,华为手机打开超级终端搜索设备时,开发板都能收到广播信息:

  #coap_listen_task#:: HandleReadEvent(UDP ServerSocket Event), serverFd[0]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[coap_discover] HandleReadEvent Begin:
         HandleReadEvent [3-1] CoapSocketRecv() 391 Bytes to recvBuffer
         HandleReadEvent [3-2] COAP_SoftBusDecode(recvBuffer to decodePacket): COAP_UDP
         HandleReadEvent [3-3] PostServiceDiscover(decodePacket): ResponseService
                 decodePacket: 
                   protocol[COAP_UDP]
                   token   [0:(null)]
                   payload [354:{"deviceId":"{\"UDID\":\"E36AD77903F.............02983\"}","devicename":"HUAWEI Mate 40E","hicomversion":"3.1.0.0","mode":1,"deviceHash":"285...........3123","serviceData":"port:41215","extendServiceDat

[coap_discover] HandleReadEvent End.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

我不懂APP上的开发,所以是直接在系统服务层上搞的,不知道怎么响应发现端的广播。

 

 

已于2021-6-26 12:02:58修改
回复
2021-6-26 12:01:29
丨张明亮丨
丨张明亮丨 回复了 liangkz_梁开祝
我的菜~ 另,请教一下,有没有让华为手机直接与Hi3516/Hi3861开发板进行交互的办法? 我本地的Hi3516/Hi3861开发板,publish服务之后,华为手机打开超级终端搜索设备时,开发板都能收到广播信息: #coap_listen_task#:: HandleReadEvent(UDP ServerSocket Event), serverFd[0]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[coap_discover] HandleReadEvent Begin: HandleReadEvent ...

我和你想法一样,最后发现要连接华为手机的软总线需要认证。

有个recv()函数是作为API提供的,而这里获取不到,就没法接入,或许这以后是商业部分。

回复
2021-6-26 16:30:26
mb6089531537bda
mb6089531537bda

那岂不是连不上了

回复
2021-6-28 09:19:05
wx60d98ec273f63
wx60d98ec273f63

连接成功了吗?

回复
2021-6-28 17:19:11
丨张明亮丨
丨张明亮丨 回复了 wx60d98ec273f63
连接成功了吗?

可以连接,但是认证失败。

回复
2021-7-1 14:39:48
爱吃土豆丝的打工人
爱吃土豆丝的打工人

如果是华为的开发板是不是会省去认证这个环节,好像华为自己的开发板是不需要认证这一层程序的。

回复
2021-7-1 15:57:52
丨张明亮丨
丨张明亮丨 回复了 爱吃土豆丝的打工人
如果是华为的开发板是不是会省去认证这个环节,好像华为自己的开发板是不需要认证这一层程序的。

是在Hi3516和Hi3861上测试的

回复
2021-7-6 09:41:56
wx6131ddd0a14f8
wx6131ddd0a14f8

膜拜大佬,请教个问题,现在2.0里面的dsoftbus和softbus_lite的区别是什么?是不是dsoftbus是发现端的代码,而softbus_lite是被发现端(轻量级设备)的代码?

回复
2021-9-3 16:59:55
Compound_Jo
Compound_Jo 回复了 丨张明亮丨
可以连接,但是认证失败。

那要自己编译一个rom才能构建一个完整iot网络?

回复
2021-9-22 19:11:06
Soon_L
Soon_L

大神有在openharmony3.0上试看看吗?

回复
2021-10-14 20:53:33
ITACMI
ITACMI 回复了 wx6131ddd0a14f8
膜拜大佬,请教个问题,现在2.0里面的dsoftbus和softbus_lite的区别是什么?是不是dsoftbus是发现端的代码,而softbus_lite是被发现端(轻量级设备)的代码?

softbus_lite应该是临时的,后面的能力会收编进dsoftbus中

回复
2022-8-27 21:58:01
回复
    相关推荐