分布式软总线核心代码分析(三)
(组网)
前言
前面分析了从设备发布到设备发现的业务流程,设备发现会触发软总线业务链路的第三步---组网
发布服务 ──→ 发现服务 ──→ 组网 ──→ 传输
(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(¶->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(¶->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(¶->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 *)¶->addr, NULL, SOFTBUS_NETWORK_JOIN_REQUEST_ERR);
NotifyStateForSession(¶->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 *)¶->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 |
| 状态机 | 创建新的,走完整流程 | 复用已有的,只做认证 |
组网状态机部分暂时保留
更多推荐



所有评论(0)