栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

软总线源码分析6:发现模块(二)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

软总线源码分析6:发现模块(二)

继上一章节分析完PublishService接口后,本章节我们开始分析StartDiscovery接口。

StartDiscovery接口

根据g_discCoapFuncInterface的接口可以知道,当为主动发现时,调用CoapStartAdvertise函数,当为被动发现时,调用CoapSubscribe函数完成对应的处理。下面我们分别介绍这两种实现:

CoapStartAdvertise

CoapStartAdvertise的源码如下所示:

static int32_t CoapStartAdvertise(const SubscribeOption *option)
{
    if (option == NULL || g_subscribeMgr == NULL) {
        return SOFTBUS_INVALID_PARAM;
    }

    if (pthread_mutex_lock(&(g_subscribeMgr->lock)) != 0) {
        LOG_ERR("pthread mutex lock failed.");
        return SOFTBUS_LOCK_ERR;
    }
    if (RegisterAllCapBitmap(CAPABILITY_NUM, option->capabilityBitmap, g_subscribeMgr, MAX_CAP_NUM) != SOFTBUS_OK) {
        (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
        LOG_ERR("merge discovery capability failed.");
        return SOFTBUS_DISCOVER_COAP_MERGE_CAP_FAIL;
    }
    if (g_subscribeMgr->isUpdate) {
        if (DiscCoapSetFilterCapability(CAPABILITY_NUM, g_subscribeMgr->allCap) != SOFTBUS_OK) {
            (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
            LOG_ERR("set all filter capability to dfinder failed.");
            return SOFTBUS_DISCOVER_COAP_SET_FILTER_CAP_FAIL;
        }
    }
    if (DiscCoapStopDiscovery() != SOFTBUS_OK) {
        (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
        LOG_ERR("coap stop discovery failed.");
        return SOFTBUS_DISCOVER_COAP_STOP_DISCOVER_FAIL;
    }
    if (DiscCoapStartDiscovery(ACTIVE_DISCOVERY) != SOFTBUS_OK) {
        (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
        LOG_ERR("coap start advertise failed.");
        return SOFTBUS_DISCOVER_COAP_START_DISCOVER_FAIL;
    }
    (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
    LOG_INFO("coap start active discovery.");
    return SOFTBUS_OK;
}

该函数主要做了四件事:

  1. 调用RegisterAllCapBitmap将能力注册到g_subscribeMgr中。
  2. 调用DiscCoapSetFilterCapability将能力保存到本地的g_filterCapabilityBitmap中。
  3. 调用DiscCoapStopDiscovery停止上一轮的发现处理。
  4. 调用DiscCoapStartDiscovery完成发现处理。

RegisterAllCapBitmap函数和DiscCoapSetFilterCapability函数比较简单,因此不展开介绍。我们主要对DiscCoapStopDiscovery函数和DiscCoapStartDiscovery函数展开。

DiscCoapStopDiscovery

DiscCoapStopDiscovery调用NSTACKX_StopDeviceFind函数,该函数使用PostEvent提交一个事件,其handle为DeviceDiscoverStopInner函数,该函数最终调用CoapServiceDiscoverStopInner函数完成处理,其源码如下:

void CoapServiceDiscoverStopInner(void)
{
    TimerSetTimeout(g_discoverTimer, 0, NSTACKX_FALSE);
    CoapServiceDiscoverStop();
    LOGI(TAG, "device discover stopped");
}

该函数首先调用TimerSetTimeout关闭掉上一轮发现设置的定时器,然后调用CoapServiceDiscoverStop函数,CoapServiceDiscoverStop的源码如下:

static void CoapServiceDiscoverStop(void)
{
    g_discoverCount = 0;
    g_forceUpdate = NSTACKX_FALSE;
    SetModeInfo(DISCOVER_MODE);
    ClearDevices(GetDeviceDBBackup());
    LOGW(TAG, "clear device list backup");
    
    g_userRequest = NSTACKX_FALSE;
}

该函数也比较简单,即清除掉各种标志和设备列表备份中的记录。

DiscCoapStartDiscovery

停止上一轮的发现处理之后,便调用DiscCoapStartDiscovery函数进行发现处理,该函数我们在上一章节介绍CoapPublish时介绍过,该函数在StartDiscovery调用的情况下,根据传入的参数,调用NSTACKX_StartDeviceFind函数。NSTACKX_StartDeviceFind函数使用PostEvent提交一个事件,其处理的handle为DeviceDiscoverInner函数,其源码如下:

static void DeviceDiscoverInner(void *argument)
{
    (void)argument;
    CoapServiceDiscoverInner(INNER_DISCOVERY);

    
    if (!IsWifiApConnected()) {
        NotifyDeviceFound(NULL, 0);
    }
}

该函数首先调用CoapServiceDiscoverInner进行发现的处理,然后判断如果当前设备的Wifi和Ble都没有打开的话,则通知用户发现的设备为空。

CoapServiceDiscoverInner的源码如下:

void CoapServiceDiscoverInner(uint8_t userRequest)
{
    uint32_t discoverInterval;
    if (!IsWifiApConnected() || g_context == NULL) {
        return;
    }

    if (userRequest) {
        g_userRequest = NSTACKX_TRUE;
        g_forceUpdate = NSTACKX_TRUE;
    }

    if (g_coapDiscoverTargetCount > 0 && g_discoverCount >= g_coapDiscoverTargetCount) {
        g_discoverCount = 0;
        SetModeInfo(DISCOVER_MODE);
        ClearDevices(GetDeviceDBBackup());
        LOGW(TAG, "clear device list backup");
        TimerSetTimeout(g_discoverTimer, 0, NSTACKX_FALSE);
    }

    if (g_discoverCount) {
        
        return;
    } else {
        
        if (BackupDeviceDB() != NSTACKX_EOK) {
            LOGE(TAG, "backup device list fail");
            return;
        }
        ClearDevices(GetDeviceDB());
        LOGW(TAG, "clear device list");
        g_coapDiscoverTargetCount = g_coapMaxDiscoverCount;
    }
    SetModeInfo(DISCOVER_MODE);
    if (CoapPostServiceDiscover() != NSTACKX_EOK) {
        LOGE(TAG, "failed to post service discover request");
        return;
    }

    discoverInterval = GetDiscoverInterval(g_discoverCount);
    if (TimerSetTimeout(g_discoverTimer, discoverInterval, NSTACKX_FALSE) != NSTACKX_EOK) {
        LOGE(TAG, "failed to set timer for service discover");
        return;
    }
    ++g_discoverCount;
    LOGI(TAG, "the first time for device discover.");

    return;
}

因为g_discoverCount在CoapServiceDiscoverStop函数中置0,因此CoapServiceDiscoverInner首先调用BackupDeviceDB,将g_deviceList中的设备内容备份到g_deviceListBackup中;然后清除掉当前g_deviceList中发现的设备;最后调用CoapPostServiceDiscover完成发现处理,并设置定时器超时门限。

 CoapPostServiceDiscover的源码我们在上一章节CoapPublish中介绍过,即组一条广播报文将消息发送出去,因此本章不展开。

上一章节的CoapPublish中,调用CoapServiceDiscoverInnerAn函数时,也会在最后设置一个定时器和对应的超时门限,那么这个定时器的作用是什么呢?

我们可以看一下CoapDiscoverInit中,创建g_discoverTimer定时器的处理。CoapDiscoverInit调用TimerStart函数为g_discoverTimer创建了一个定时器,其对应的超时处理函数为CoapServiceDiscoverTimerHandle,源码如下:

static void CoapServiceDiscoverTimerHandle(void *argument)
{
    uint32_t discoverInterval;

    (void)argument;

    if (g_discoverCount >= g_coapDiscoverTargetCount || !IsWifiApConnected()) {
        
        CoapServiceDiscoverStop();
        return;
    }

    if (CoapPostServiceDiscover() != NSTACKX_EOK) {
        LOGE(TAG, "failed to post service discover request");
        goto L_ERR_SERVICE_DISCOVER;
    }
    LOGI(TAG, "the %d times for device discover.", g_discoverCount + 1);

    
    discoverInterval = GetDiscoverInterval(g_discoverCount);

    ++g_discoverCount;
    if (TimerSetTimeout(g_discoverTimer, discoverInterval, NSTACKX_FALSE) != NSTACKX_EOK) {
        LOGE(TAG, "failed to set timer for service discover");
        goto L_ERR_SERVICE_DISCOVER;
    }
    return;

L_ERR_SERVICE_DISCOVER:
    
    LOGE(TAG, "abort service discover, have tried %u request", g_discoverCount);
    
    g_discoverCount = 0;
    return;
}

该函数处理也很简单,即判断g_discoverCount是否超过了g_coapDiscoverTargetCount(该值被设为默认值12),如果没超过则继续调用CoapPostServiceDiscover对外广播,若已超过则直接返回。

因此我们知道,发现的处理会对外广播最多12次COAP报文,并且会根据当前的次数调用GetDiscoverInterval调整时间,前两次间隔100ms,第3次和第4次间隔300ms,后面每次间隔500ms。

CoapSubscribe

CoapSubscribe的源码如下所示:

static int32_t CoapSubscribe(const SubscribeOption *option)
{
    if (option == NULL || g_subscribeMgr == NULL) {
        return SOFTBUS_INVALID_PARAM;
    }

    if (pthread_mutex_lock(&(g_subscribeMgr->lock)) != 0) {
        LOG_ERR("pthread mutex lock failed.");
        return SOFTBUS_LOCK_ERR;
    }
    if (RegisterAllCapBitmap(CAPABILITY_NUM, option->capabilityBitmap, g_subscribeMgr, MAX_CAP_NUM) != SOFTBUS_OK) {
        (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
        LOG_ERR("merge discovery capability failed.");
        return SOFTBUS_DISCOVER_COAP_MERGE_CAP_FAIL;
    }
    if (g_subscribeMgr->isUpdate) {
        if (DiscCoapSetFilterCapability(CAPABILITY_NUM, g_subscribeMgr->allCap) != SOFTBUS_OK) {
            (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
            LOG_ERR("set all filter capability to dfinder failed.");
            return SOFTBUS_DISCOVER_COAP_SET_FILTER_CAP_FAIL;
        }
    }
    (void)pthread_mutex_unlock(&(g_subscribeMgr->lock));
    LOG_INFO("coap start passive discovery.");
    return SOFTBUS_OK;
}

因为CoapSubscribe为被动发现场景,因此CoapSubscribe不需要对外广播广文。CoapSubscribe的函数只干了两件事情:1.调用RegisterAllCapBitmap将能力注册到g_subscribeMgr中;2.调用DiscCoapSetFilterCapability将能力保存到本地的g_filterCapabilityBitmap中。

OnDeviceFound接口

我们之前在CoapPublish和StartDiscovery接口中发现,主动发布和主动发现都会向外部广播报文,而被动发布和被动发现只会将能力集保存在本地。那么本地的设备是如何发现外部设备的呢?我们可以顺着OnDeviceFound接口找到答案。

OnDeviceFound的触发链

当对端设备收到了Coap报文后,根据报文携带的URI和method,使用对应的handler进行处理。在软总线服务加载的时候,关于发现的handler注册如下:

coap_resource_t *r = NULL;

r = coap_resource_init(coap_make_str_const(COAP_DEVICE_DISCOVER_URI), g_resourceFlags);
if (r == NULL) {
    return;
}
coap_register_handler(r, COAP_REQUEST_POST, HndPostServiceDiscover);
coap_resource_set_get_observable(r, NSTACKX_TRUE);
coap_add_resource(ctx, r);

因此对端设备调用HndPostServiceDiscover函数完成报文对应的处理。其源码如下:

static void HndPostServiceDiscover(coap_context_t *ctx, struct coap_resource_t *resource, coap_session_t *session,
    coap_pdu_t *request, coap_binary_t *token, coap_string_t *query, coap_pdu_t *response)
{
    (void)ctx;
    (void)resource;
    (void)session;
    (void)token;
    (void)query;
    if (request == NULL || response == NULL) {
        return;
    }
    char *remoteUrl = NULL;
    DeviceInfo deviceInfo;
    if (HndPostServiceDiscoverInner(request, &remoteUrl, &deviceInfo) != NSTACKX_EOK) {
        free(remoteUrl);
        return;
    }
    if (GetModeInfo() == PUBLISH_MODE_UPLINE || GetModeInfo() == PUBLISH_MODE_OFFLINE) {
        LOGD(TAG, "local is not DISCOVER_MODE");
        free(remoteUrl);
        return;
    }
    if (UpdateDeviceDb(&deviceInfo, g_forceUpdate) != NSTACKX_EOK) {
        free(remoteUrl);
        return;
    }
    if (g_forceUpdate) {
        g_forceUpdate = NSTACKX_FALSE;
    }
    if (deviceInfo.mode == PUBLISH_MODE_PROACTIVE) {
        LOGD(TAG, "peer is PUBLISH_MODE_PROACTIVE");
        free(remoteUrl);
        return;
    }
    if (remoteUrl != NULL) {
        CoapResponseService(remoteUrl);
        free(remoteUrl);
    } else {
        response->code = COAP_RESPONSE_CODE(COAP_RESPONSE_201);
    }
}

该函数的工作主要如下:

  1. 调用HndPostServiceDiscoverInner将Coap报文的data数据转为deviceInfo
  2. 调用UpdateDeviceDb函数更新本地维护的g_deviceList
  3. 如果对端的报文是发布模式,则直接退出。如果是发现模式,则调用CoapResponseService函数向对端返回响应报文。

这里我们主要对第2点的处理展开,UpdateDeviceDb的源码如下:

int32_t UpdateDeviceDb(const DeviceInfo *deviceInfo, uint8_t forceUpdate)
{
    DeviceInfo *internalDevice = NULL;
    int8_t updated = NSTACKX_FALSE;

    if (deviceInfo == NULL) {
        return NSTACKX_EINVAL;
    }

    internalDevice = GetDeviceInfoById(deviceInfo->deviceId, g_deviceList);
    if (internalDevice == NULL) {
        internalDevice = CreateNewDevice(deviceInfo);
        if (internalDevice == NULL) {
            return NSTACKX_ENOMEM;
        }
        updated = NSTACKX_TRUE;
    } else {
        if (UpdateDeviceInfo(internalDevice, deviceInfo, &updated) != NSTACKX_EOK) {
            return NSTACKX_EFAILED;
        }
    }
    internalDevice->update = updated;

    if (updated || forceUpdate) {
        DeviceListChangeHandle();
    }

    return NSTACKX_EOK;
}

UpdateDeviceDb首先根据对端设备的deviceId,首先查找本地的g_deviceList。如果没有该设备则先创建一个设备,如果有该设备则更新该设备信息。然后调用DeviceListChangeHandle通知设备列表发生变化。

DeviceListChangeHandle的源码如下:

static void DeviceListChangeHandle(void)
{
    NSTACKX_DeviceInfo deviceList[NSTACKX_MAX_DEVICE_NUM];
    uint32_t count = NSTACKX_MAX_DEVICE_NUM;

    (void)memset_s(deviceList, sizeof(deviceList), 0, sizeof(deviceList));
    GetDeviceList(deviceList, &count, true);

    NotifyDeviceListChanged(deviceList, count);
    if (CoapDiscoverRequestOngoing()) {
        NotifyDeviceFound(deviceList, count);
    }
}

该函数主要工作如下:

  1. 调用GetDeviceList函数将g_deviceList列表中能力与本地能力不匹配的设备过滤掉。
  2. 调用NotifyDeviceListChanged通知本地设备列表发生变化。
  3. 如果本地正在进行发现处理,则调用NotifyDeviceFound通知本地找到了新设备。

我们主要针对第2步展开,第3步NotifyDeviceFound当前默认执行空操作。NotifyDeviceListChanged执行g_parameter中的onDeviceListChanged钩子函数进行处理,该函数为OnDeviceFound。

至此OnDeviceFound的调用链我们追踪清楚了,下面我们主要分析OnDeviceFound的实现。

OnDeviceFound的实现

OnDeviceFound的源码如下:

static void OnDeviceFound(const NSTACKX_DeviceInfo *deviceList, uint32_t deviceCount)
{
    if (deviceCount == 0) {
        return;
    }

    for (uint32_t i = 0; i < deviceCount; i++) {
        const NSTACKX_DeviceInfo *nstackxDeviceInfo = deviceList + i;
        if (nstackxDeviceInfo == NULL) {
            return;
        }
        if (((nstackxDeviceInfo->update) & 0x1) == 0) {
            LOG_INFO("duplicate  device is not reported.");
            continue;
        }
        DeviceInfo discDeviceInfo;
        (void)memset_s(&discDeviceInfo, sizeof(DeviceInfo), 0, sizeof(DeviceInfo));
        if (memcpy_s(discDeviceInfo.devName, sizeof(discDeviceInfo.devName),
                     nstackxDeviceInfo->deviceName, sizeof(nstackxDeviceInfo->deviceName)) != EOK ||
            memcpy_s(discDeviceInfo.capabilityBitmap, sizeof(discDeviceInfo.capabilityBitmap),
                     nstackxDeviceInfo->capabilityBitmap, sizeof(nstackxDeviceInfo->capabilityBitmap))) {
            LOG_ERR("memcpy_s failed.");
            return;
        }
        discDeviceInfo.addrNum = 1;
        discDeviceInfo.devType = nstackxDeviceInfo->deviceType;
        discDeviceInfo.capabilityBitmapNum = nstackxDeviceInfo->capabilityBitmapNum;
        discDeviceInfo.addr[0].type = CONNECT_ADDR_WLAN;
        if (ParseDeviceUdid(nstackxDeviceInfo, &discDeviceInfo) != SOFTBUS_OK) {
            LOG_ERR("parse device udid failed.");
            return;
        }
        if (ParseReservedInfo(nstackxDeviceInfo, &discDeviceInfo) != SOFTBUS_OK) {
            LOG_ERR("parse reserve information failed.");
            return;
        }
        if (g_discCoapInnerCb != NULL) {
            g_discCoapInnerCb->OnDeviceFound(&discDeviceInfo);
        }
    }
}

该函数跳过设备列表中不需要更新的设备,然后对需要更新的设备解析其json文件,将其转换为DeviceInfo类型,然后一次调用g_discCoapInnerCb的OnDeviceFound回调函数。该回调函数为DiscOnDeviceFound,其源码如下:

void DiscOnDeviceFound(const DeviceInfo *device)
{
    uint32_t tmp;
    DiscInfo *infoNode = NULL;
    for (tmp = 0; tmp < CAPABILITY_MAX_BITNUM; tmp++) {
        if (IsBitmapSet((uint32_t *)&(device->capabilityBitmap[0]), tmp) == false) {
            continue;
        }
        LIST_FOR_EACH_ENTRY(infoNode, &(g_capabilityList[tmp]), DiscInfo, capNode) {
            LOG_INFO("find callback:id = %d", infoNode->id);
            InnerDeviceFound(infoNode, device);
        }
    }
    return;
}

即根据该设备的capabilityBitmap,在每个能力对应的g_capabilityList下,对每个节点调用InnerDeviceFound函数进行处理,InnerDeviceFound的源码如下:

static void InnerDeviceFound(const DiscInfo *infoNode, const DeviceInfo *device)
{
    uint32_t tmp;
    bool isInnerInfo = false;
    for (tmp = 0; tmp < MODULE_MAX; tmp++) {
        if (strcmp(infoNode->item->packageName, g_discModuleMap[tmp]) != 0) {
            continue;
        }
        isInnerInfo = true;
    }
    if (isInnerInfo == false) {
        infoNode->item->callback.subscribeCb.OnServerDeviceFound(infoNode->item->packageName, device);
        return;
    }
    if (infoNode->item->callback.innerCb.OnDeviceFound == NULL) {
        LOG_ERR("OnDeviceFound not regist");
        return;
    }
    infoNode->item->callback.innerCb.OnDeviceFound(device);
}

该函数判断InfoNode是否为内部的节点,如果是则调用OnDeviceFound回调,否则调用OnServerDeviceFound回调。

当前内部进行发现处理时,其OnDeviceFound回调为空。外部进行发现处理时,OnServerDeviceFound回调为SoftBusClientStub的OnDeviceFound函数。

总结

至此,发现模块我们就剖析完了。简而言之:

  1. publishService和StartDiscovery都有主动和被动的区别。主动的接口都会对外部广播报文,而被动的接口设置完本地的能力集后就什么都没干了。
  2. 主动的publishService和StartDiscovery都会向外部广播最大12次报文,当对端收到报文后会根据报文内容更新其设备列表,并调用OnDeviceFound通知上层应用。
  3. publishService和StartDiscovery的区别在于:对端收到的如果是publishService的报文,并不会返回响应,而如果收到的是StartDiscovery的报文的话,则会将自己的设备信息单播给源端。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/861714.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号