(组网)

前言

前面分析了从设备发布到设备发现的业务流程,设备发现会触发软总线业务链路的第三步---组网

发布服务 ──→ 发现服务 ──→ 组网 ──→ 传输
 (Publish)   (Discover)  (Join)   (Transmit)

发现服务到组网的衔接点

Discovery 层发现设备
  │
  ▼
DiscOnDeviceFound(device)                    ← disc_manager.c:406
  │  匹配 capability → 通知订阅者
  │
  ▼
LNN Discovery Manager
  │ DeviceFound(addr, infoReport)            ← lnn_discovery_manager.c:63
  │   → LnnNotifyDiscoveryDevice(addr, infoReport, true) ->触发组网的桥梁
  │
  ▼
Net Builder (组网模块)
  │ LnnNotifyDiscoveryDevice()               ← lnn_net_builder.c:1128
  │   → 创建 JoinLnnMsgPara
  │   → PostBuildMessageToHandler(MSG_TYPE_DISCOVERY_DEVICE)
  │
  ▼
TrySendJoinLNNRequest()                      ← lnn_net_builder.c:237
  │   → FindConnectionFsmByAddr()  查找/创建连接状态机
  │   → PostJoinRequestToConnFsm() 发起认证连接
  │
  ▼
LnnConnectionFsm (连接状态机)
  │   → AuthStartVerify()  认证
  │   → 认证成功 → 设备上线
  │
  ▼
LnnNotifyOnlineState()                       ← bus_center_event.c
  │   → 通知所有注册了 EVENT_NODE_STATE_ONLINE 的模块
  │
  ▼
传输层可用

组网模块的核心文件

文件 职责
lnn_net_builder.c 组网主逻辑:发现设备→发起连接→认证
lnn_net_builder_process.c 消息处理:处理 MSG_TYPE_DISCOVERY_DEVICE 等消息
lnn_connection_fsm.c 连接状态机:管理连接的完整生命周期
lnn_connection_fsm_process.c 状态机状态转换处理
lnn_sync_info_manager.c 设备信息同步
lnn_topo_manager.c 网络拓扑管理

DiscManager 发现设备后,LNN 层注册的回调 g_discCb.OnDeviceFound 被调用,经过两层 DeviceFound 函数中转,最终到达 LnnNotifyDiscoveryDevice,发现服务是组网的前提,再次声明,在5.1版本中不存在startdiscovery这种函数,见我之前的文章,manager给系统的接口没有startdiscovery,不信谣,不传谣。

LnnNotifyDiscoveryDevice 

函数签名

int32_t LnnNotifyDiscoveryDevice(
    const ConnectionAddr *addr,          // 发现设备的连接地址(IP+端口 / BLE MAC)
    const LnnDfxDeviceInfoReport *infoReport,  // 设备诊断信息(设备类型等)
    bool isNeedConnect)                 // 是否需要发起连接(true=主动组网,false=仅记录)

逐行解读

int32_t LnnNotifyDiscoveryDevice(...)
{
    // 1. 地址校验
    if (LnnIsConnectionAddrInvalid(addr)) {
        return SOFTBUS_INVALID_PARAM;
    }

    // 2. 模块初始化检查
    if (g_netBuilder.isInit == false) {
        return SOFTBUS_NO_INIT;
    }

    // 3. 诊断信息校验
    if (infoReport == NULL) {
        return SOFTBUS_INVALID_PARAM;
    }

    // 4. 构造消息参数
    para = CreateJoinLnnMsgPara(addr, infoReport, DEFAULT_PKG_NAME, isNeedConnect, false);
    //       填充内容:
    //         para->pkgName      = "com.huawei.nearby" (默认包名)
    //         para->isNeedConnect = isNeedConnect (是否需要连接)
    //         para->isForceJoin  = false (不强制加入)
    //         para->addr         = 设备地址
    //         para->infoReport   = 诊断信息

    // 5. 投递异步消息到组网线程
    PostBuildMessageToHandler(MSG_TYPE_DISCOVERY_DEVICE, para);
    //       消息类型: MSG_TYPE_DISCOVERY_DEVICE
    //       处理函数: ProcessDevDiscoveryRequest()
}

para结构体

typedef struct {
    char pkgName[PKG_NAME_SIZE_MAX];
    bool isNeedConnect;
    bool isSession;
    bool isForceJoin;
    ConnectionAddr addr;
    NodeInfo *dupInfo;
    LnnDfxDeviceInfoReport infoReport;
} JoinLnnMsgPara;

函数功能

LnnNotifyDiscoveryDevice 是发现服务到组网流程的桥梁函数,它将发现的设备地址封装为异步消息投递到组网线程,由组网线程的 ProcessDevDiscoveryRequest 接手,真正开始组网。这个函数不做任何组网操作,只做一件事:构造消息参数,投递到组网线程的消息队列

LnnNotifyDiscoveryDevice()           ← 调用方线程(可能是介质回调线程)
  │
  │  构造 JoinLnnMsgPara
  │  PostBuildMessageToHandler(MSG_TYPE_DISCOVERY_DEVICE, para)
  │
  ▼
组网线程消息队列
  │  ...
  │  MSG_TYPE_DISCOVERY_DEVICE + para
  │  ...
  ▼
NetBuilderMessageHandler()           ← 组网线程取出消息
  │  g_messageProcessor[MSG_TYPE_DISCOVERY_DEVICE]
  ▼
ProcessDevDiscoveryRequest()         ← lnn_net_builder_process.c:259
  │  TrySendJoinLNNRequest(para, false, false)
  ▼
真正的组网逻辑开始...

为什么用异步消息LnnNotifyDiscoveryDevice 可能被介质回调线程调用,而组网涉及认证、状态机等耗时操作,不能在回调线程中阻塞执行,所以通过消息队列投递到组网线程异步处理。

void NetBuilderMessageHandler(SoftBusMessage *msg)

连接机制

PostBuildMessageToHandler()                    ← lnn_net_builder.c:127
  │
  │  1. CreateNetBuilderMessage(msgType, para)  ← lnn_net_builder.c:113
  │     创建 SoftBusMessage:
  │       msg->what    = msgType (如 MSG_TYPE_DISCOVERY_DEVICE)
  │       msg->obj     = para
  │       msg->handler = &g_netBuilder.handler   ← 关键: 绑定了 handler
  │
  │  2. g_netBuilder.looper->PostMessage(looper, msg)
  │     将消息投递到 Looper 消息队列
  │
  ▼
Looper 线程取出消息
  │  根据 msg->handler 找到 g_netBuilder.handler
  │  调用 handler.HandleMessage(msg)
  │
  ▼
NetBuilderMessageHandler(msg)                  ← lnn_net_builder_process.c:825
  │
  │  g_messageProcessor[msg->what](msg->obj)
  │
  ▼
对应的处理函数

函数注册绑定位置

static int32_t InitNetBuilderLooper(void)
{
    ...
    LnnGetNetBuilder()->looper = GetLooper(LOOP_TYPE_DEFAULT);     // 获取默认 Looper
    LnnGetNetBuilder()->handler.name = "NetBuilderHandler";
    LnnGetNetBuilder()->handler.looper = LnnGetNetBuilder()->looper;
    LnnGetNetBuilder()->handler.HandleMessage = NetBuilderMessageHandler;  // ← 绑定在这里
    ...
}

函数功能

核心代码就最后一行,按照消息类型查表调用对应的函数。

消息分发表

下标 消息类型 处理函数 业务含义
0 MSG_TYPE_JOIN_LNN ProcessJoinLNNRequest 主动加入网络
1 MSG_TYPE_DISCOVERY_DEVICE ProcessDevDiscoveryRequest 发现设备→触发组网
2 MSG_TYPE_CLEAN_CONN_FSM ProcessCleanConnectionFsm 清理连接状态机
3 MSG_TYPE_VERIFY_RESULT ProcessVerifyResult 认证结果处理
4 MSG_TYPE_DEVICE_VERIFY_PASS ProcessDeviceVerifyPass 设备认证通过
5 MSG_TYPE_DEVICE_DISCONNECT ProcessDeviceDisconnect 设备断连
6 MSG_TYPE_DEVICE_NOT_TRUSTED ProcessDeviceNotTrusted 设备不可信
7 MSG_TYPE_LEAVE_LNN ProcessLeaveLNNRequest 主动离开网络
8 MSG_TYPE_SYNC_OFFLINE_FINISH ProcessSyncOfflineFinish 离线同步完成
9 MSG_TYPE_NODE_STATE_CHANGED ProcessNodeStateChanged 节点状态变化
10 MSG_TYPE_MASTER_ELECT ProcessMasterElect 主节点选举
11 MSG_TYPE_LEAVE_INVALID_CONN ProcessLeaveInvalidConn 离开无效连接
12 MSG_TYPE_LEAVE_BY_ADDR_TYPE ProcessLeaveByAddrType 按地址类型离开
13 MSG_TYPE_LEAVE_SPECIFIC ProcessLeaveSpecific 离开指定连接
14 MSG_TYPE_LEAVE_BY_AUTH_ID ProcessLeaveByAuthId 按认证ID离开
15 MSG_TYPE_RE_SYNC_DEVICE_NAME ProcessSetReSyncDeviceName 重新同步设备名

按业务阶段分类

入网阶段 (加入):
  MSG_TYPE_JOIN_LNN          → ProcessJoinLNNRequest
  MSG_TYPE_DISCOVERY_DEVICE  → ProcessDevDiscoveryRequest    
  MSG_TYPE_VERIFY_RESULT     → ProcessVerifyResult
  MSG_TYPE_DEVICE_VERIFY_PASS→ ProcessDeviceVerifyPass

在线阶段 (维护):
  MSG_TYPE_NODE_STATE_CHANGED→ ProcessNodeStateChanged
  MSG_TYPE_MASTER_ELECT      → ProcessMasterElect
  MSG_TYPE_RE_SYNC_DEVICE_NAME → ProcessSetReSyncDeviceName
  MSG_TYPE_SYNC_OFFLINE_FINISH → ProcessSyncOfflineFinish

离网阶段 (离开):
  MSG_TYPE_LEAVE_LNN         → ProcessLeaveLNNRequest
  MSG_TYPE_DEVICE_DISCONNECT → ProcessDeviceDisconnect
  MSG_TYPE_DEVICE_NOT_TRUSTED→ ProcessDeviceNotTrusted
  MSG_TYPE_LEAVE_INVALID_CONN→ ProcessLeaveInvalidConn
  MSG_TYPE_LEAVE_BY_ADDR_TYPE→ ProcessLeaveByAddrType
  MSG_TYPE_LEAVE_SPECIFIC    → ProcessLeaveSpecific
  MSG_TYPE_LEAVE_BY_AUTH_ID  → ProcessLeaveByAuthId

清理阶段:
  MSG_TYPE_CLEAN_CONN_FSM    → ProcessCleanConnectionFsm

组网核心函数链

ProcessDevDiscoveryRequest(para)                    ← lnn_net_builder_process.c:256
  │  只有一行: 转发
  ▼
TrySendJoinLNNRequest(para, false, false)           ← lnn_net_builder.c:237
  │
  │  ★ 这是组网的核心分发函数,决定走哪条路径
  │
  ├─ 路径1: 没有已有连接 → 创建新连接状态机
  ├─ 路径2: 已有连接且在线 → 可能重新认证
  ├─ 路径3: 并发数已满 → 挂起等待
  └─ 路径4: WiFi 账号变更 → 重新认证

代码分析

int32_t TrySendJoinLNNRequest(const JoinLnnMsgPara *para, bool needReportFailure, bool isShort)
{
    // 1. 根据是否需要连接决定 isShort
    //    isNeedConnect=true  → isShort=false (长连接,需要组网)
    //    isNeedConnect=false → isShort=true  (短连接,仅记录)
    isShort = para->isNeedConnect ? false : true;

    // 2. 查找是否已有该地址的连接状态机
    LnnConnectionFsm *connFsm = FindConnectionFsmByAddr(&para->addr, isShort);

    // 3. 三种分支

分支一

没有已有连接/分支已死/设备信息变更

    if (connFsm == NULL || connFsm->isDead || CheckRemoteBasicInfoChanged(para->dupInfo)) {
        // 3a. 并发数已满?→ 挂起等待
        if (TryPendingJoinRequest(para, needReportFailure)) {
            return SOFTBUS_OK;    // 等待后续处理
        }
        // 3b. 创建新连接状态机 或 复用已死的连接
        ret = PostJoinRequestToConnFsm(connFsm, para, needReportFailure);
        return ret;
    }
分支1
  │
  ├─ TryPendingJoinRequest() 返回 true
  │   → 当前并发连接数已达上限
  │   → 将请求挂入 g_netBuilder.pendingList 等待队列
  │   → 等某个连接完成(上线或失败)后再取出执行
  │   → 直接返回,不创建新状态机
  │
  └─ TryPendingJoinRequest() 返回 false
      → 并发数未满,可以立即处理
      → PostJoinRequestToConnFsm(connFsm, para, needReportFailure)
      → 创建新的连接状态机 或 复用已死的状态机
      → 启动状态机,进入 AUTH 认证状态
PostJoinRequestToConnFsm
函数功能

创建或复用连接状态机,并向它发送 JOIN_REQUEST 消息,正式启动组网

1.查找状态机状态

再检查一次有没有关联可用的状态机

  if (connFsm == NULL) {
        connFsm = FindConnectionFsmByAddr(&para->addr, false);
    }

2.判定条件满足,创建新的状态机

connFsm == NULL || connFsm->isDead || 设备信息变更?
    → StartNewConnectionFsm(¶->addr, pkgName, isNeedConnect)
       ★ 这是创建连接状态机的核心函数
       ★ 创建后状态机处于初始状态,等待 START 消息
    → 复制设备信息: connFsm->connInfo.dupInfo = DupNodeInfo(para->dupInfo)
    → isCreate = true  ← 标记本次是新创建的
if (connFsm == NULL || connFsm->isDead || CheckRemoteBasicInfoChanged(para->dupInfo)) {
        connFsm = StartNewConnectionFsm(&para->addr, para->pkgName, para->isNeedConnect);
        if (connFsm != NULL) {
            connFsm->connInfo.dupInfo = (para->dupInfo == NULL) ? NULL : DupNodeInfo(para->dupInfo);
        }
        isCreate = true;
    }

3.标记会话类型

 para->isSession? → connFsm->isSession = true

4.发送 JOIN_REQUEST 到状态机

if (connFsm == NULL || LnnSendJoinRequestToConnFsm(connFsm, false) != SOFTBUS_OK)
 LnnSendJoinRequestToConnFsm(connFsm, false)
    → 向状态机投递 FSM_CTRL_MSG_START 消息
    → 状态机收到后进入 AUTH 认证状态

4.1发送JOIN_REQUEST 失败

发送失败?
    → needReportFailure? → LnnNotifyJoinResult() 通知上层组网失败
    → isCreate? → 销毁刚创建的状态机,回滚
if (needReportFailure) {
            LnnNotifyJoinResult((ConnectionAddr *)&para->addr, NULL, SOFTBUS_NETWORK_JOIN_REQUEST_ERR);
            NotifyStateForSession(&para->addr);
        }
if (connFsm != NULL && isCreate) {
            LnnFsmRemoveMessageByType(&connFsm->fsm, FSM_CTRL_MSG_START);
            ListDelete(&connFsm->node);
            --LnnGetNetBuilder()->connCount;
            LnnDestroyConnectionFsm(connFsm);
        }
rc = SOFTBUS_NETWORK_JOIN_REQUEST_ERR;

4.2 发送JOIN_REQUEST成功

记录诊断信息 + 设置连接标志
    → JOIN_REQUEST: 用户主动发起的组网
    → JOIN_AUTO:    自动触发的组网(如发现服务触发)
if (rc == SOFTBUS_OK) {
        connFsm->connInfo.infoReport = para->infoReport;
        connFsm->connInfo.flag |=
            (needReportFailure ? LNN_CONN_INFO_FLAG_JOIN_REQUEST : LNN_CONN_INFO_FLAG_JOIN_AUTO);
        LNN_LOGI(LNN_BUILDER, "infoReport.osType=%{public}d, infoReport.type=%{public}X",
            connFsm->connInfo.infoReport.osType, connFsm->connInfo.infoReport.type);
    }

核心流程图

PostJoinRequestToConnFsm(connFsm, para, needReportFailure)
  │
  ├─ connFsm 为空? → 再查一次
  │
  ├─ 仍为空/已死/信息变更?
  │   │
  │   ▼
  │  StartNewConnectionFsm()          ← 创建新连接状态机
  │    │
  │    │  内部做了:
  │    │  ├─ LnnCreateConnectionFsm()   分配内存,初始化状态机
  │    │  ├─ 设置4个状态的处理函数
  │    │  │   AUTH → AuthStateProcess
  │    │  │   CLEAN_INVALID_CONN → CleanInvalidConnStateProcess
  │    │  │   ONLINE → OnlineStateProcess
  │    │  │   LEAVING → LeavingStateProcess
  │    │  ├─ ListAdd(&g_netBuilder.fsmList)  加入全局状态机链表
  │    │  └─ LnnFsmStart()                   启动状态机线程
  │    │
  │    ▼
  │  connFsm 已创建,处于初始状态,等待消息
  │
  ▼
 LnnSendJoinRequestToConnFsm(connFsm, false)
    │
    │  向状态机投递 FSM_CTRL_MSG_START 消息
    │
    ▼
 状态机线程收到消息                  <-状态机部分
    │
    ▼
 AuthStateProcess()                 ← 进入认证状态,发起认证
    │
    ▼
 AuthStartVerify()                  ← 真正开始认证

分支二

已有连接且在线

    if ((connFsm->connInfo.flag & LNN_CONN_INFO_FLAG_ONLINE) != 0) {
        // WiFi 连接 → 刷新认证信息
        if (connFsm->connInfo.addr.type == CONNECTION_ADDR_WLAN || ...) {
            AuthFlushDevice(uuid);
        }
        // 发送 JOIN_REQUEST 到状态机(可能触发重新同步)
        LnnSendJoinRequestToConnFsm(connFsm, para->isForceJoin);
    }
WiFi/以太网连接情况下

WiFi 连接的设备可能因为网络切换、密钥更新等原因导致认证信息过期,需要刷新确保后续通信正常。BLE/USB 连接不需要此操作。

if (connFsm->connInfo.addr.type == CONNECTION_ADDR_WLAN || connFsm->connInfo.addr.type == CONNECTION_ADDR_ETH) {
            char uuid[UUID_BUF_LEN] = {0};
            (void)LnnConvertDlId(connFsm->connInfo.peerNetworkId, CATEGORY_NETWORK_ID, CATEGORY_UUID,
                uuid, UUID_BUF_LEN);
            (void)AuthFlushDevice(uuid);
        }
步骤 说明
LnnConvertDlId 将 peerNetworkId 转换为 UUID
AuthFlushDevice 用 UUID 刷新认证设备的密钥/会话信息
向状态机发送 JOIN_REQUEST
if ((LnnSendJoinRequestToConnFsm(connFsm, para->isForceJoin) != SOFTBUS_OK) && needReportFailure) {
            LNN_LOGE(LNN_BUILDER, "online status, process join lnn request failed");
            LnnNotifyJoinResult((ConnectionAddr *)&para->addr, NULL, SOFTBUS_NETWORK_JOIN_REQUEST_ERR);
        }

设备已在线,但仍然发送 JOIN_REQUEST,可能触发:

  • 重新同步设备信息(设备名、能力等可能已变化)
  • 强制重连isForceJoin=true 时)

失败时通知上层组网结果。

通知会话层

如果这次发现请求来自会话层(isSession=true),直接通知会话层"该设备已就绪",因为设备已在线,不需要再等认证。

    if (para->isSession && para->dupInfo != NULL) {
        LnnNotifyStateForSession(para->dupInfo->deviceInfo.deviceUdid, SOFTBUS_OK);
    }
场景总结
设备A 和 设备B 已经组网在线
  │
  │  设备A 再次发现 设备B 的广播
  │  (可能是周期性广播、或能力变更广播)
  ▼
TrySendJoinLNNRequest 进入分支2
  │
  ├─ WiFi连接 → AuthFlushDevice()   刷新认证信息
  ├─ 发送 JOIN_REQUEST              触发信息重新同步
  └─ 会话请求 → 通知会话层就绪       跳过认证,直接可用

分支三

WiFi 账号变更 → 重新认证

    if (addr.type == CONNECTION_ADDR_WLAN &&
        IsNeedWifiReauth(connFsm->connInfo.peerNetworkId, addr.peerUid, ...)) {
        // 直接发起重新认证
        AuthStartVerify(&authConn, requestId, LnnGetReAuthVerifyCallback(), ...);
    }
ConnectionAddr addr = para->addr;

拷贝地址的值,后面para要释放

if (addr.type != CONNECTION_ADDR_WLAN ||
    !IsNeedWifiReauth(connFsm->connInfo.peerNetworkId, addr.peerUid, MAX_ACCOUNT_HASH_LEN)) {
    LNN_LOGI(LNN_BUILDER, "account not change no need reauth");
    FreeJoinLnnMsgPara(para);
    return SOFTBUS_OK;
}

退出条件

条件 含义
addr.type != CONNECTION_ADDR_WLAN 非 WiFi 连接,不存在账号变更问题,直接返回
!IsNeedWifiReauth(...) WiFi 连接但账号未变更,不需要重新认证,直接返回

为什么只有 WiFi 需要重新认证:WiFi 连接依赖账号体系(如华为账号),账号切换意味着信任关系可能变化,需要重新认证。BLE/USB 连接不依赖账号,无需此处理。

重新认证

AuthConnInfo authConn;
uint32_t requestId = AuthGenRequestId();
LnnConvertAddrToAuthConnInfo(&addr, &authConn);
DfxRecordLnnAuthStart(&authConn, para, requestId);
FreeJoinLnnMsgPara(para);
return AuthStartVerify(&authConn, requestId, LnnGetReAuthVerifyCallback(), AUTH_MODULE_LNN, false);
步骤 说明
AuthGenRequestId() 生成认证请求 ID
LnnConvertAddrToAuthConnInfo 将 ConnectionAddr 转换为认证层需要的 AuthConnInfo
DfxRecordLnnAuthStart 记录诊断信息
FreeJoinLnnMsgPara 释放参数(之后不再使用)
AuthStartVerify 发起重新认证,注意回调是 LnnGetReAuthVerifyCallback

三个分支完整对比

TrySendJoinLNNRequest
  │
  ├─ 分支1: 无已有连接
  │   → 创建新连接状态机 → 进入 AUTH 状态 → 认证
  │   场景: 首次发现设备
  │
  ├─ 分支2: 已在线
  │   → 刷新认证 + 重新同步 + 通知会话层
  │   场景: 设备已组网,再次收到广播
  │
  └─ 分支3: WiFi 账号变更
      → AuthStartVerify() 重新认证
      场景: 同一设备但登录账号变了,信任关系需要重新建立

分支三与分支一区别

对比 分支1(新连接) 分支3(重新认证)
认证方式 通过状态机 AuthStateProcess 发起 直接调用 AuthStartVerify
回调 LnnGetVerifyCallback LnnGetReAuthVerifyCallback
状态机 创建新的,走完整流程 复用已有的,只做认证

组网状态机部分暂时保留

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐