3. 多屏异显硬件基础
多屏互动系统里,最让人头疼的问题是什么?我个人觉得,是「设备找不到设备」。你想想看,手机想投屏到电视,电视得先知道手机在哪;电脑要扩展屏幕到平板,平板得先被电脑发现。这个「发现」的过程,就是本章要聊的核心——局域网设备发现协议。
说白了,设备发现就是让网络里的各个节点互相打个招呼:「嘿,我在这,我能干这些事。」没有这个机制,后面的连接、传输、同步全是空谈。我在做第一个多屏原型时,就栽在这个坑里——设备死活发现不了对方,折腾了两天才发现是广播地址写错了。
3.1 局域网设备发现机制
局域网设备发现,本质上解决两个问题:
- 谁在线?——网络里有哪些设备处于可连接状态。
- 能干什么?——每个设备支持哪些功能(投屏、扩展、镜像、控制)。
常见的发现机制有三种,我列个表对比一下:
| 机制 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| UDP 广播 | 向子网广播地址发消息,所有设备都能收到 | 实现简单,无需中心节点 | 广播风暴风险,跨网段无效 | 小型局域网,设备数 < 50 |
| UDP 组播 | 加入特定组播组,只向组内成员发送 | 节省带宽,可跨网段 | 需要 IGMP 支持,配置稍复杂 | 中型网络,设备数 50~200 |
| mDNS/DNS-SD | 基于多播 DNS 的服务发现 | 零配置,苹果生态原生支持 | 依赖系统服务,延迟不稳定 | 跨平台、消费级场景 |
嗯,这里要注意:不要一上来就选最复杂的。我见过不少团队直接上 mDNS,结果在 Windows 上兼容性出问题,最后又改回 UDP 广播。对于多屏互动这种小规模局域网场景(通常 2~10 台设备),UDP 广播是最务实的选择。
3.2 基于UDP广播的发现协议
UDP 广播的原理很简单:设备往 255.255.255.255 或子网广播地址发一个数据包,同一子网内所有设备都能收到。收到后,设备判断是不是给自己的消息,是就回复,不是就丢弃。
我习惯把发现协议分成两个阶段:
- 探测阶段(Probe)——新设备上线,主动发广播:「有人吗?」
- 应答阶段(Response)——在线设备收到后,回复:「我在,这是我的信息。」
这里有个关键点:探测消息要周期性发送。为什么?因为设备可能随时上线或下线。我一般设置 3~5 秒发一次探测,连续 3 次没收到应答就认为设备离线。这个「心跳机制」能保证设备列表实时更新。
3.3 自定义协议格式
UDP 广播只是传输方式,真正要落地,还得定义一套协议格式。说白了,就是约定好「消息长什么样,每个字段什么意思」。
我设计过一套协议,结构如下:
+----------------+----------------+----------------+----------------+
| Magic Number | Version | Message Type | Payload Len |
| (4 bytes) | (1 byte) | (1 byte) | (2 bytes) |
+----------------+----------------+----------------+----------------+
| Payload (变长) |
| (设备信息、能力列表、时间戳等) |
+-------------------------------------------------------------------+
各字段说明:
- Magic Number:固定值
0x4D534443(即 "MSDC" 的 ASCII),用来快速校验是不是本协议的消息。收到包先检查这个,不对直接丢弃。 - Version:协议版本号,方便后续升级兼容。我一般从
0x01开始。 - Message Type:消息类型,比如
0x01表示探测,0x02表示应答,0x03表示离线通知。 - Payload Len:负载长度,单位字节。接收方根据这个值知道要读多少数据。
- Payload:实际数据,用 JSON 或 Protobuf 编码。我推荐 JSON,调试方便,解析简单。
Payload 的 JSON 结构示例:
{
"device_id": "TV-LivingRoom-001",
"device_name": "客厅电视",
"device_type": "display",
"ip": "192.168.1.100",
"port": 9000,
"capabilities": ["screen_mirror", "extend_display", "remote_control"],
"timestamp": 1712345678
}
3.4 完整发现流程
把上面这些串起来,一个完整的设备发现流程是这样的:
- 设备 A 上线,启动 UDP 监听端口 9000。
- 设备 A 每 3 秒向
255.255.255.255:9000发送探测包(Type=0x01)。 - 设备 B 收到探测包,校验 Magic Number 和 Version,通过后解析 Payload。
- 设备 B 发现是探测消息,立即回复应答包(Type=0x02),带上自己的设备信息。
- 设备 A 收到应答,更新本地设备列表,显示「客厅电视已连接」。
- 设备 B 下线时,发送离线通知包(Type=0x03),设备 A 收到后移除该设备。
这个流程看起来简单,但实际落地时有很多细节。比如:收到重复的应答怎么办?我习惯用 device_id 做去重,同一个 ID 只保留最新一条记录。再比如:设备突然断电,没来得及发离线通知怎么办?那就靠心跳超时——连续 9 秒没收到心跳,就认为设备离线。
3.5 协议设计中的常见陷阱
做协议设计这么多年,我踩过不少坑。列几个典型的:
- Payload 太大——UDP 单包最大 65535 字节,但实际网络 MTU 通常是 1500 字节。超过 MTU 的包会被分片,分片丢失会导致整个包作废。所以 Payload 最好控制在 1400 字节以内。
- 没有版本协商——协议升级后,老设备发来的包新设备不认识。我建议在应答包里带上「支持的最高版本」,双方协商使用共同版本。
- 忽略安全——UDP 广播是明文传输,任何人都能伪造发现包。生产环境至少要加一个简单的 Token 校验,防止恶意设备混入。
- 端口写死——万一端口被占用,整个发现机制就废了。我习惯让设备尝试绑定端口,失败则自动 +1 重试,并在 Payload 里告知实际端口。
3.6 本章知识结构图
下面这张 SVG 图把本章的核心逻辑串起来了,你可以对照着理解:
这张图从左到右展示了发现机制、协议格式、完整流程三个核心模块,以及探测、应答、离线三个具体阶段。你可以把它当作本章的「知识地图」,写代码时对照着来,不容易漏掉关键环节。
3.7 小结
设备发现是多屏互动的第一步,也是最重要的一步。UDP 广播虽然「土」,但在小规模局域网里足够稳定、足够快。自定义协议时,记得把 Magic Number、版本号、消息类型、负载长度这四个字段作为标配,Payload 用 JSON 方便调试,后期再考虑换成 Protobuf 压缩体积。