前言:
经典的dsoftbus 使用WiFi链路作为发现、设备发现的网络层,本文结合代码简析其作用
1. 模块架构分层
在 dsoftbus的通信栈中,CoAP 起到了承上启下的关键作用。它向下屏蔽了 WiFi 物理链路的复杂性,向上为设备发现(Discovery)提供标准的信令通道。
1.1协议报文在网络层的封装
2. 网络层的操作
2.1 网络接口的动态感知与绑定 (Interface Binding)
DSoftBus 并非简单地监听 0.0.0.0,而是采用特定接口绑定 (Interface Binding) 策略。这允许它在复杂的网络环境(如同时连接 WiFi 和热点)中精确控制数据流向。
核心机制:UpdateLocalNetworkInterface
该函数是网络层与应用层的"铰链"。它轮询系统网络接口,一旦发现 IP 变化,立即触发 CoAP 服务的重置。
- 源码逻辑分析 (
nstackx_device.c):- 遍历接口 :系统启动或收到
Netlink事件时,遍历wlan0,p2p0,eth0等接口。 - IP 校验 :检查接口是否分配了有效 IP (
ipAddr.s_addr != 0)。 - 定向初始化 :调用
CoapServerInitWithIdx,传入具体的 IP 地址。这意味着 DSoftBus 会为每个有效接口创建一个独立的 CoAP Server 实例。
- 遍历接口 :系统启动或收到
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| void UpdateLocalNetworkInterface() { foreach (interface in system_interfaces) { if (interface.has_ip && interface.ip_changed) { // 销毁旧的 Socket CoapServerDestroy(interface.old_ip); // 在新 IP 上重新绑定 CoAP 服务 CoapServerInitWithIdx(interface.new_ip, interface.index); } } } |
2.2 Socket 创建与广播机制 (Socket & Broadcast)
在局域网设备发现中,DSoftBus 优先选择广播 (Broadcast) 而非组播。这是因为广播在家庭路由器中的兼容性最好,且无需路由器支持 IGMP Snooping。
核心实现:CoapCreateUdpClientEx
- Socket 类型 :
SOCK_DGRAM(UDP)。 - 端口绑定 :默认绑定
5683(CoAP 标准端口)。如果被占用,可能会回退到动态端口。 - 广播使能 :必须显式调用
setsockopt(SO_BROADCAST),否则操作系统会拦截发往255.255.255.255的包。
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| // 关键代码路径: components/nstackx/nstackx_ctrl/core/mini_discover/coap_app.c static int32_t CoapCreateUdpClientEx(...) { int fd = socket(AF_INET, SOCK_DGRAM, 0); // 关键:绑定到特定网卡的 IP,而不是 INADDR_ANY // 这确保了广播包只从该网卡发出,不会“串台” bind(fd, (struct sockaddr *)&localAddr, ...); // 关键:开启广播权限 int opt = 1; setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); return fd; } |
2.3 广播地址的获取策略
DSoftBus 如何知道往哪里发包?
- 策略 :它通过
ioctl(SIOCGIFBRDADDR)获取当前接口的子网广播地址 (例如192.168.1.255),而不是全局广播地址255.255.255.255。 - 优势:子网广播通常会被路由器限制在当前局域网段内,不会造成更大范围的网络风暴。
3. 链路层操作
3.1 多网卡上下文管理 (Context Management)
DSoftBus 为了支持同时在 WiFi Station (连接路由器) 和 WiFi P2P (直连) 模式下工作,设计了多实例上下文管理机制。
-
全局管理数组 :
g_coapCtxArr[NSTACKX_MAX_LISTENED_NIF_NUM]- 这是一个全局数组,每个元素对应一个物理网卡接口(如
wlan0,p2p0)。 - 每个元素包含独立的
coap_context_t(libcoap 上下文) 和epoll任务句柄。
- 这是一个全局数组,每个元素对应一个物理网卡接口(如
-
隔离与并发:
- 当
UpdateLocalNetworkInterface检测到新网卡时,会分配一个空闲的index。 - 调用
CoapServerInitWithIdx为该index创建独立的 Socket 和 CoAP 栈。 - 优势 : 即使
wlan0网络拥塞或出错,也不会影响p2p0上的设备发现,实现了故障隔离。
- 当
3.2 资源注册与路由 (Resource & Routing)
CoAP 服务器启动后,必须注册"资源"才能响应外部请求。这类似于 Web 服务器注册 URL 路由。
- 核心函数 :
CoapInitResourcesInner(位于coap_discover.c)
| 对应 Handler | 作用 | 传输类型 |
|---|---|---|
HndPostServiceDiscover |
设备发现。处理对方发来的"你在哪?"请求,回复设备信息。 | Non-confirmable (广播) |
HndPostServiceMsg |
服务消息。用于设备间协商 Session 参数、Auth 握手等关键指令。 | Confirmable (单播) |
4. 网络数据流转
4.1 接收流程:从网卡到业务回调
这是一个典型的异步事件驱动流程:
- Epoll 触发 : 内核检测到 UDP Socket 有数据,唤醒
CoAPEpollReadHandle。 - Libcoap 处理 : 调用
coap_io_do_io->recvfrom读取数据包。 - 协议解析 : Libcoap 解析 CoAP Header,根据 URI (
/d/d) 找到对应的 Handler。 - 业务处理 (
HndPostServiceMsgEx) :- Payload 解析 : 调用
ParseServiceMsgFrame解析 JSON 数据。 - 去重 :
RefreshMsgIdList过滤重复的广播包。 - 上报 : 调用
NotifyMsgReceived将结构化的DeviceInfo传递给上层 (Discovery Service)。
- Payload 解析 : 调用

如上图发送流程:从业务到网卡
- 业务触发 : 上层调用
CoapSendServiceMsgWithDefiniteTargetIp。 - 报文构建 :
CreateServiceMsgFrame将 C 结构体序列化为 JSON 字符串。 - 路由选择 : 根据目标 IP 选择对应的
coap_context_t(即选择从哪个网卡发出)。 - CoAP 封装: 添加 CoAP Header (Type=CON, Code=POST, ID=...)。
- 物理发送 :
sendto将 UDP 包发出。
4.2 json关键数据格式
CoAP 只是信封,真正的信件内容是 JSON 格式的 Payload。
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| { "deviceId": "UDID-Hash-Value...", // 设备唯一标识 "moduleName": "softbus", // 模块名称 "data": { // 业务数据 "devName": "My Phone", "devType": 14, // 14代表手机 "cap": 15 // 能力位图 (Bitmap) } } |
5. 发现流程的全生命周期
从 WiFi 连接成功的那一刻起,CoAP 层是如何响应并启动发现的?如下图:

6. 协议报文在网络层的封装
当 DSoftBus 发出一个发现请求时,线路上的比特流结构如下:
| 层级 | 协议头 | 关键字段值 | 作用 |
|---|---|---|---|
| L2 | Ethernet | Dest MAC: FF:FF:FF:FF:FF:FF |
链路层广播,确保 WiFi 芯片接收该帧 |
| L3 | IP | Src IP: 192.168.1.100 Dst IP: 192.168.1.255 |
网络层广播,确保同一子网内的主机接收 |
| L4 | UDP | Src Port: 5683 Dst Port: 5683 |
传输层定位,确保由 CoAP 服务处理 |
| App | CoAP | Type: NON (Non-confirmable) Method: POST URI: /d/d |
应用层指令,告知这是一个"设备发现"请求 |
| Data | Payload | {"devId":"...", "cap":...} |
业务数据,携带设备身份和能力信息 |
7. 总结:
DSoftBus 在 CoAP 与网络层的结合上体现了**"分而治之,精准控制"**的设计哲学:
- 不依赖全局广播:通过绑定特定网卡 IP 并计算子网广播地址,实现了对多网卡环境(如同时开启 WiFi 和 热点)的精准隔离。
- 动态热插拔:基于事件驱动的接口管理机制,使得设备在网络切换(如从家里带到公司)时,能毫秒级重启发现服务,用户无感。
- 标准与高效并存:利用标准的 Socket 接口和 CoAP 协议,但在实现上针对局域网广播场景做了深度优化。