分布式软总线组网流程源码解析(一)

📅 2026/6/26 4:56:09
分布式软总线组网流程源码解析(一)
系统开机分布式软总线在开机的时候系统会直接调用onstart()函数,实现如下void SoftBusServer::OnStart() { COMM_LOGI(COMM_SVC, SoftBusServer OnStart called!); InitSoftBusServer(); if (!Publish(this)) { COMM_LOGE(COMM_SVC, SoftBusServer publish failed!); } IPCSkeleton::SetMaxWorkThreadNum(SOFTBUS_IPC_THREAD_NUM); }初始化软总线配置这个里面调用的InitSoftBusServer()函数就是初始化软总线的配置的这个函数内部实现像LnnInitMonitorInit、InitServicesAndModules、void InitSoftBusServer(void) { SoftbusConfigInit();//初始化软总线配置系统 LnnInitMonitorInit();//初始化LNN局域网初始化监控器 if (ServerStubInit() ! SOFTBUS_OK) { //初始化默认返回SOFTBUS_OK运行时覆盖本函数用于建立IPC通道 COMM_LOGE(COMM_SVC, server stub init failed.); return; } if (SoftBusTimerInit() ! SOFTBUS_OK) { //定时器系统 COMM_LOGE(COMM_SVC, softbus timer init failed.); return; } if (LooperInit() ! SOFTBUS_OK) { //事件循环系统 COMM_LOGE(COMM_SVC, softbus looper init failed.); return; } if (InitDdos() ! SOFTBUS_OK) { //安全防护系统 COMM_LOGE(COMM_SVC, softbus ddos init failed.); } //核心服务与模块初始化 int32_t ret InitServicesAndModules(); if (ret ! SOFTBUS_OK) { ServerModuleDeinit();//初始化失败会清除已初始化模块 COMM_LOGE(COMM_SVC, softbus framework init failed, err %{public}d, ret); return; } ret SoftBusBtInit(); if (ret ! SOFTBUS_OK) { LnnInitModuleReturnSet(INIT_DEPS_BLUETOOTH, ret); LnnInitModuleStatusSet(INIT_DEPS_BLUETOOTH, DEPS_STATUS_FAILED); ServerModuleDeinit(); COMM_LOGE(COMM_SVC, softbus bt init failed, err %{public}d, ret); return; } LnnInitModuleStatusSet(INIT_DEPS_BLUETOOTH, DEPS_STATUS_SUCCESS); LnnModuleInitMonitorCheckStart(); g_isInit true; //初始化成功标志 COMM_LOGI(COMM_SVC, softbus framework init success.); }这里面最后一段的蓝牙初始化特殊些因为他集成了LNN监控机制LnnInitModuleReturnSet/LnnInitModuleStatusSet向 LNN 初始化监控器报告蓝牙模块的初始化状态和错误码LnnModuleInitMonitorCheckStart检查所有依赖模块是否都已成功初始化如果是则触发后续的组网流程整体初始化流程图InitSoftBusServer() │ ├─ SoftbusConfigInit() ← 配置 ├─ LnnInitMonitorInit() ← 监控器 ├─ ServerStubInit() ← IPC桩 (失败→退出) ├─ SoftBusTimerInit() ← 定时器 (失败→退出) ├─ LooperInit() ← 事件循环 (失败→退出) ├─ InitDdos() ← DDOS防护 (失败→仅日志) │ ├─ InitServicesAndModules() ← 核心服务链 (失败→清理退出) │ ├─ ConnServerInit ← 连接 │ ├─ AuthInit ← 认证 │ ├─ DiscServerInit ← 发现 │ ├─ BusCenterServerInit ← 组网 │ ├─ TransServerInit ← 传输 │ ├─ WifiDirect / BleDirect ← 直连 │ └─ DFX ← 可维护性 │ ├─ SoftBusBtInit() ← 蓝牙 (失败→清理退出状态上报) │ └─ g_isInit true ← 完成核心模块初始化顺序上面的InitServicesAndModules初始化函数的初始化顺序是严格依照依赖顺序实现static int32_t InitServicesAndModules(void) { if (ConnServerInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus conn server init failed.); return SOFTBUS_CONN_SERVER_INIT_FAILED; } if (AuthInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus auth init failed.); return SOFTBUS_AUTH_INIT_FAIL; } if (DiscServerInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus disc server init failed.); return SOFTBUS_DISC_SERVER_INIT_FAILED; } if (BusCenterServerInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus buscenter server init failed.); return SOFTBUS_CENTER_SERVER_INIT_FAILED; } if (TransServerInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus trans server init failed.); return SOFTBUS_TRANS_SERVER_INIT_FAILED; } if (DiscEventManagerInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus disc event manager init failed.); return SOFTBUS_DISCOVER_MANAGER_INIT_FAIL; } if (GetWifiDirectManager()-init() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus wifi direct init failed.); return SOFTBUS_WIFI_DIRECT_INIT_FAILED; } if (ConnBleDirectInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus ble direct init failed.); return SOFTBUS_CONN_BLE_DIRECT_INIT_FAILED; } if (InitSoftbusSysEvt() ! SOFTBUS_OK || SoftBusHiDumperInit() ! SOFTBUS_OK) { COMM_LOGE(COMM_SVC, softbus dfx init failed.); return SOFTBUS_DFX_INIT_FAILED; } InstRegister(NULL); return SOFTBUS_OK; }顺序模块说明1ConnServerInit连接管理BLE/WiFi/BR等2AuthInit设备认证、鉴权初始化3DiscServerInit设备发现4BusCenterServerInit软总线控制中心组网管理5TransServerInit传输服务传输权限、会话管理、传输通道、传输质量初始化6DiscEventManagerInit发现事件管理服务初始化7GetWifiDirectManager.initWiFi Direct8ConnBleDirectInitBLE Direct9InitSoftbusSysEvt HiDumperInitDFX可维护性连接 → 认证 → 发现 → 组网 → 传输失败时调用ServerModuleDeinit反向清理模块发布服务/L0/foundation/communication/dsoftbus/sdk/frame/standard/src/softbus_server_proxy_standard.cpp整体架构理解┌──────────────┐ IPC (SendRequest) ┌──────────────────┐ │ Client Side │ ──────────────────────► │ Server Side │ │ (Proxy) │ │ (Stub) │ │ │ │ │ │ clientStub │ ◄─── callback ──────── │ 通过 clientStub │ │ (单例) │ │ 回调客户端 │ └──────────────┘ └──────────────────┘该函数的核心目的是客户端向 SoftBus 服务端注册自己同时将客户端的回调 stub 传递给服务端以便服务端后续能通过该 stub 回调客户端。抽象理解就是一个设备或者应用有一个什么能力比如拍照这个应用将自己的服务注册到分布式网络中希望被其他设备使用。谁发布调用者即服务提供方。它是一个应用或服务进程拥有某种能力如文件分享、播放控制希望被其他设备使用接收方分布式软总线SoftBusServerProxyFrame指向的远端服务即软总线服务端softbus_server。它是系统级的核心服务负责管理所有设备的服务注册信息充当“服务管理中心”。执行流程拆解int32_t SoftBusServerProxyFrame::SoftbusRegisterService(const char *clientPkgName, const sptrIRemoteObject object) { sptrIRemoteObject remote Remote(); if (remote nullptr) { COMM_LOGE(COMM_SDK, remote is nullptr!); return SOFTBUS_IPC_ERR; } sptrIRemoteObject clientStub SoftBusServerProxyFrame::GetRemoteInstance(); if (clientStub nullptr) { COMM_LOGE(COMM_SDK, client stub is nullptr!); return SOFTBUS_IPC_ERR; } MessageParcel data; if (!data.WriteInterfaceToken(GetDescriptor())) { COMM_LOGE(COMM_SDK, SoftbusRegisterService write InterfaceToken failed!); return SOFTBUS_TRANS_PROXY_WRITETOKEN_FAILED; } if (!data.WriteRemoteObject(clientStub)) { COMM_LOGE(COMM_SDK, SoftbusRegisterService write remote object failed!); return SOFTBUS_TRANS_PROXY_WRITEOBJECT_FAILED; } if (!data.WriteCString(clientPkgName)) { COMM_LOGE(COMM_SDK, SoftbusRegisterService write clientPkgName failed!); return SOFTBUS_TRANS_PROXY_WRITECSTRING_FAILED; } MessageParcel reply; MessageOption option; int32_t err remote-SendRequest(MANAGE_REGISTER_SERVICE, data, reply, option); if (err ! 0) { COMM_LOGE(COMM_SDK, SoftbusRegisterService send request failed!); return SOFTBUS_IPC_ERR; } int32_t serverRet 0; if (!reply.ReadInt32(serverRet)) { COMM_LOGE(COMM_SDK, SoftbusRegisterService read serverRet failed!); return SOFTBUS_TRANS_PROXY_READINT_FAILED; } return serverRet; }1、获取远端代理对象通过Remote获取与软总线服务端通信的IPC代理对象如果获取失败则返回SOFTBUS_IPC_ERR。remote作用是获取通向soft_bus_server的IPC代理。相当于获取远端总线的大门如果remote nullptr就是系统挂掉了连门都找不到直接返回失败。2.获取唯一的单例sptrIRemoteObject clientStub SoftBusServerProxyFrame::GetRemoteInstance();这个clientstub应该是客户端与服务端通信的唯一实例是softbus服务端依据客户端包名来唯一索引的回调通道clientstub与remote组成了双向IPC。3.打包注册协议MessageParcel data向服务端发送注册申请单要准备接口令牌WriteInterfaceToken、单例stubWriteRemoteObject、应用包名WriteCString4.同步递交申请并等待批准int32_t err remote-SendRequest(MANAGE_REGISTER_SERVICE, data, reply, option);MANAGE_REGISTER_SERVICE接口命令符softbus根据这个调用底层对应业务逻辑data上面提到的申请单reply容器存放服务端回调过来的结果optionMessageOption控制本次ipc的行为模式返回值err与reply中的serverRet区别err函数返回值代表IPC 通信层是否成功。例如0表示发送成功、服务端正常响应非0表示远端进程死亡、驱动错误或超时与业务逻辑无关。serverRet从 reply 读取代表服务端业务逻辑层的处理结果。例如0表示注册成功-1表示包名无权限与业务功能相关。拉起发布服务上面客户端发布服务以后下层服务端根据传参会路由到对应的业务代码OnRemoteRequest就是负责这个功能int32_t SoftBusServerStub::OnRemoteRequest( uint32_t code, MessageParcel data, MessageParcel reply, MessageOption option) { SoftbusRecordCalledApiCnt(code); if (data.ReadInterfaceToken() ! GetDescriptor()) { COMM_LOGE(COMM_SVC, SOFTBUS_SERVER_NOT_INIT ReadInterfaceToken failed!); return SOFTBUS_IPC_ERR; } if (!GetServerIsInit()) { COMM_LOGE(COMM_SVC, server not init); if (!reply.WriteInt32(SOFTBUS_SERVER_NOT_INIT)) { COMM_LOGE(COMM_SVC, SOFTBUS_SERVER_NOT_INIT write reply failed!); } return SOFTBUS_IPC_ERR; } auto itPerm memberPermissionMap_.find(code); if (itPerm ! memberPermissionMap_.end()) { const char *permission itPerm-second; uint32_t callingTokenId IPCSkeleton::GetCallingTokenID(); if ((permission ! nullptr) (!SoftBusCheckIsAccessAndRecordAccessToken(callingTokenId, permission))) { SoftbusReportPermissionFaultEvt(code); COMM_LOGE(COMM_SVC, access token permission denied! permission%{public}s, tokenId%{public}d, permission, callingTokenId); pid_t callingPid OHOS::IPCSkeleton::GetCallingPid(); TransAlarmExtra extra { .callerPid (int32_t)callingPid, .methodId (int32_t)code, .conflictName NULL, .conflictedName NULL, .occupyedName NULL, .permissionName permission, .sessionName NULL, }; TRANS_ALARM(NO_PERMISSION_ALARM, CONTROL_ALARM_TYPE, extra); return SOFTBUS_ACCESS_TOKEN_DENIED; } } const auto itFunc memberFuncMap_.find(code); if (itFunc ! memberFuncMap_.end()) { auto memberFunc itFunc-second; if (memberFunc ! nullptr) { return (this-*memberFunc)(data, reply); } } COMM_LOGI(COMM_SVC, default case, need check.); return IPCObjectStub::OnRemoteRequest(code, data, reply, option); }流程SoftbusRecordCalledApiCnt(code);这个code就是对应上面的指令码核对接口令牌if (data.ReadInterfaceToken() ! GetDescriptor())检查服务端初始化GetServerIsInit()AccessToken权限校验itPerm是从memberPermissionMap_中查找该code对应的权限名称如ohos.permission.DISTRIBUTED_DATASYNCmemberPermissionMap_是一个map容器用来做指令键值比对。callingTokenId获取调用方的tokenID.权限验证SoftBusCheckIsAccessAndRecordAccessToken(callingTokenId, permission权限被拒绝时SoftbusReportPermissionFaultEvt(code)记录权限故障事件 TRANS_ALARM触发NO_PERMISSION_ALARM告警 返回SOFTBUS_ACCESS_TOKEN_DENIED请求分发return (this-*memberFunc)(data, reply);这一句写的好对象内部都有一个this指针这个项目自定义map容器是键是指令值是具体的函数指针他这个是直接在map中索引调用函数。比如这里的发布服务会索引memberFuncMap_[SERVER_PUBLISH_LNN] SoftBusServerStub::PublishLNNInner;前面的申请表传到服务端是字节流 SoftBusServerStub::PublishLNNInner(会进行反序列化客户端进程 服务端进程 ┌──────────────────┐ ┌──────────────────┐ │ PublishInfo 对象 │ │ │ │ ┌──────────────┐ │ 序列化 │ MessageParcel │ │ │ publishId1 │ │ ──────────→ │ (一串字节流) │ │ │ modeACTIVE │ │ (扁平化) │ 1|1|0|2|... │ │ │ mediumWIFI │ │ │ │ │ │ freqHIGH │ │ 反序列化 │ ┌──────────────┐ │ │ │ capability..│ │ ←────────── │ │ publishId1 │ │ │ └──────────────┘ │ (重组) │ │ modeACTIVE │ │ │ │ │ │ mediumWIFI │ │ │ │ │ │ freqHIGH │ │ │ │ │ └──────────────┘ │ └──────────────────┘ └──────────────────┘每次读取一个数据都会通过COMM_CHECK_AND_RETURN_RET_LOGE进行校验和边界检查。当校验组装成功后才会调用实际发布业务这个位置有个坑看到下面以为调用的是下面的这个函数对吧int32_t retReply PublishLNN(clientName, info);再跳转会发现int32_t ISoftBusServer::PublishLNN(const char *pkgName, const PublishInfo *info) { (void)pkgName; (void)info; COMM_LOGI(COMM_SVC, ipc default impl); return SOFTBUS_IPC_ERR; }他是个默认实现直接返回错误了这是个虚函数光看实现看不出来在类声明里面这个调用会根据根据this指针到虚表里面去查实际调用对象是softbusserver默认实现一般不走这里才是真实业务int32_t SoftBusServer::PublishLNN(const char *pkgName, const PublishInfo *info) { pid_t callingPid OHOS::IPCSkeleton::GetCallingPid(); return LnnIpcPublishLNN(pkgName, (int32_t)callingPid, info); }服务端获取对端客户端的callingPid并将之前的参数都传进去int32_t LnnIpcPublishLNN(const char *pkgName, int32_t callingPid, const PublishInfo *info) { int32_t ret IsOverThreshold(pkgName, SERVER_PUBLISH_LNN); if (ret SOFTBUS_DDOS_ID_AND_USER_SAME_COUNT_LIMIT ret SOFTBUS_DDOS_USER_ID_ALL_COUNT_LIMIT) { LNN_LOGE(LNN_EVENT, heres the statistics, no need return); } return LnnPublishService(pkgName, info, false, callingPid); }ddos防护检查包名对这个发布接口的访问频率超出阈值加入日志但不停止调用流程图 LnnIpcPublishLNN() ← 你在这里 └── LnnPublishService() └── DiscPublishService() ├── 5 道参数/状态检查 ├── CreateDiscInfoForPublish() → 创建内部节点 └── InnerPublishService() → 加入列表 按介质分发 └── CallInterfaceByMedium() └── CoapPublish() / BlePublish() / ...int32_t LnnPublishService(const char *pkgName, const PublishInfo *info, bool isInnerRequest, int32_t callingPid) { LNN_CHECK_AND_RETURN_RET_LOGE( !SoftBusIsRamTest(), SOFTBUS_RAM_TEST_ABORT, LNN_BUILDER, LnnPublishService: ram test abort); int32_t ret; if (!isInnerRequest) { if ((ret DiscPublishService(pkgName, info, callingPid)) ! SOFTBUS_OK) { DfxRecordLnnDiscServiceEnd(DISC_SERVER_PUBLISH, pkgName, ret); LNN_LOGE(LNN_BUILDER, DiscPublishService failed\n); return ret; } return SOFTBUS_OK; } if ((ret DiscStartScan(MODULE_LNN, info, callingPid)) ! SOFTBUS_OK) { DfxRecordLnnDiscServiceEnd(DISC_SERVER_PUBLISH, LNN_DEFAULT_PKG_NAME, ret); LNN_LOGE(LNN_BUILDER, DiscStartScan failed\n); return ret; } return SOFTBUS_OK; }int32_t DiscPublishService(const char *packageName, const PublishInfo *info, int32_t callingPid) { // ① 参数非空检查 DISC_CHECK_AND_RETURN_RET_LOGE(packageName ! NULL info ! NULL, SOFTBUS_INVALID_PARAM, DISC_CONTROL, invalid parameters); // ② 包名长度检查 DISC_CHECK_AND_RETURN_RET_LOGW(strlen(packageName) PKG_NAME_SIZE_MAX, SOFTBUS_INVALID_PARAM, DISC_CONTROL, package name too long); // ③ 禁止内部保留包名 DISC_CHECK_AND_RETURN_RET_LOGE(!IsInnerPackageName(packageName), SOFTBUS_INVALID_PARAM, DISC_CONTROL, package name is reserved, not allowed); // ④ 发布信息合法性检查 DISC_CHECK_AND_RETURN_RET_LOGW(CheckPublishInfo(info) SOFTBUS_OK, SOFTBUS_INVALID_PARAM, DISC_CONTROL, invalid info); // ⑤ 模块初始化状态检查 DISC_CHECK_AND_RETURN_RET_LOGE(g_isInited true, SOFTBUS_DISCOVER_MANAGER_NOT_INIT, DISC_CONTROL, manager is not inited); // ⑥ 创建发布信息节点 DiscInfo *infoNode CreateDiscInfoForPublish(info, callingPid); DISC_CHECK_AND_RETURN_RET_LOGE(infoNode ! NULL, SOFTBUS_DISCOVER_MANAGER_INFO_NOT_CREATE, DISC_CONTROL, create info failed); // ⑦ 执行内部发布 int32_t ret InnerPublishService(packageName, infoNode, PUBLISH_SERVICE); if (ret ! SOFTBUS_OK) { FreeDiscInfo(infoNode, PUBLISH_SERVICE); // 失败时释放节点 } return ret; }DiscPublishService进行5项安全检查步骤检查项失败返回值含义①packageName和info非空SOFTBUS_INVALID_PARAM基本空指针防护②包名长度 PKG_NAME_SIZE_MAXSOFTBUS_INVALID_PARAM防止缓冲区溢出③包名不是内部保留名SOFTBUS_INVALID_PARAM外部 App 不能用系统保留包名④PublishInfo内容合法SOFTBUS_INVALID_PARAM检查 publishId、mode、medium、freq 等字段⑤发现管理器已初始化SOFTBUS_DISCOVER_MANAGER_NOT_INIT防止未初始化就调用⑥创建DiscInfo节点SOFTBUS_DISCOVER_MANAGER_INFO_NOT_CREATE分配内存、填充信息⑦调用InnerPublishService其返回值加入发布列表 按介质分发static int32_t InnerPublishService(const char *packageName, DiscInfo *info, const ServiceType type) { int32_t ret SoftBusMutexLock(g_publishInfoList-lock); DISC_CHECK_AND_RETURN_RET_LOGE(ret SOFTBUS_OK, SOFTBUS_LOCK_ERR, DISC_CONTROL, lock failed); do { ret AddDiscInfoToPublishList(packageName, NULL, info, type); if (ret ! SOFTBUS_OK) { DISC_LOGE(DISC_CONTROL, add info to list failed); break; } DFX_RECORD_DISC_CALL_START(info, packageName, PUBLISH_FUNC); ret CallInterfaceByMedium(info, packageName, PUBLISH_FUNC); if (ret ! SOFTBUS_OK) { DISC_LOGE(DISC_CONTROL, call interface by medium failed); ListDelete((info-node)); info-item-infoNum--; } } while (false); SoftBusMutexUnlock(g_publishInfoList-lock); return ret; }步骤操作说明①SoftBusMutexLock对全局发布列表g_publishInfoList加互斥锁保证线程安全②AddDiscInfoToPublishList将发布信息节点挂到全局链表上用于后续管理和去重③DFX_RECORD_DISC_CALL_START记录诊断打点开始时间、调用方、函数类型用于性能分析④CallInterfaceByMedium核心根据info-mediumCOAP/BLE/USB/AUTO调用对应底层发布接口⑤失败回滚如果底层发布失败从链表中删除刚加入的节点恢复计数⑥SoftBusMutexUnlock解锁DiscPublishService() ├── 5 道参数校验 ├── CreateDiscInfoForPublish() → 创建节点 └── InnerPublishService() ← 你在这里 ├── AddDiscInfoToPublishList() → 登记 └── CallInterfaceByMedium() → 执行 ├── COAP → CoapPublish() ├── BLE → BlePublish() ├── AUTO → CoAP BLE └── USB → UsbPublish()这里面链表节点的挂载和以前接触的传统链表不一样采用的是内核链表侵入式链表不过多赘述后面慢慢了解。函数指针路由器static int32_t CallInterfaceByMedium(const DiscInfo *info, const char *packageName, const InterfaceFuncType type) { int32_t ret SOFTBUS_OK; switch (info-medium) { case COAP: ret CallSpecificInterfaceFunc((info-option), g_discCoapInterface, info-mode, type); DfxCallInterfaceByMedium(info, packageName, type, ret); return ret; case BLE: ret CallSpecificInterfaceFunc((info-option), g_discBleInterface, info-mode, type); DfxCallInterfaceByMedium(info, packageName, type, ret); return ret; case AUTO: { int32_t coapRet CallSpecificInterfaceFunc((info-option), g_discCoapInterface, info-mode, type); DfxCallInterfaceByMedium(info, packageName, type, coapRet); int32_t bleRet CallSpecificInterfaceFunc((info-option), g_discBleInterface, info-mode, type); DfxCallInterfaceByMedium(info, packageName, type, bleRet); DISC_CHECK_AND_RETURN_RET_LOGE(coapRet SOFTBUS_OK || bleRet SOFTBUS_OK, SOFTBUS_DISCOVER_MANAGER_INNERFUNCTION_FAIL, DISC_CONTROL, all medium failed); return SOFTBUS_OK; } case USB: ret CallSpecificInterfaceFunc((info-option), g_discUsbInterface, info-mode, type); DfxCallInterfaceByMedium(info, packageName, type, ret); return ret; default: return SOFTBUS_DISCOVER_MANAGER_INNERFUNCTION_FAIL; } }根据传入类型调用相应处理。将执行返回值写入给IPC的reply返回给客户端回退机制return IPCObjectStub::OnRemoteRequest(code, data, reply, option);未识别的code交给父类处理发现服务流程上和发布服务类似┌─────── PublishLNN 流程 ───────┐ ┌─────── RefreshLNN 流程 ───────┐ │ │ │ │ 客户端 App 调用 │ PublishLNN() │ │ RefreshLNN() │ │ ↓ │ │ ↓ │ SDK Proxy 层 │ SendRequest(PUBLISH_LNN) │ │ SendRequest(REFRESH_LNN) │ │ ↓ │ │ ↓ │ ═══════════════════╪═══════ IPC 边界 ═══════════════╪════╪═══════ IPC 边界 ═══════════════ │ ↓ │ │ ↓ │ 服务端 Stub 层 │ PublishLNNInner() │ │ RefreshLNNInner() │ 反序列化 │ ↓ │ │ ↓ │ 服务端业务层 │ SoftBusServer::PublishLNN() │ │ SoftBusServer::RefreshLNN() │ │ ↓ │ │ ↓ │ IPC 逻辑层 │ LnnIpcPublishLNN() │ │ LnnIpcRefreshLNN() │ │ ↓ │ │ ↓ │ 发现服务接口层 │ LnnPublishService() │ │ LnnStartDiscDevice() │ │ ↓ │ │ ↓ │ 发现管理器 │ DiscPublishService() │ │ DiscStartDiscovery() │ │ ↓ │ │ ↓ │ 内部发布/发现 │ InnerPublishService() │ │ InnerStartDiscovery() │ │ ↓ │ │ ↓ │ 按介质分发 │ CallInterfaceByMedium() │ │ CallInterfaceByMedium() │ type │ PUBLISH_FUNC │ │ STARTDISCOVERTY_FUNC │ │ ↓ │ │ ↓ │ 函数指针路由 │ CallSpecificInterfaceFunc() │ │ CallSpecificInterfaceFunc() │ │ ↓ │ │ ↓ │ 底层协议 │ CoapPublish() │ │ CoapStartAdvertise() │ └────────────────────────────────┘ └────────────────────────────────┘