局域网域名方案的实现方式
方案一:路由器内置 DNS / 静态域名绑定(最简单)
大部分家用路由器支持 本地 DNS 静态条目:
路由器管理后台 → DHCP/DNS → 静态域名绑定
192.168.1.100 → mqtt.local
192.168.1.101 → device001.local
设备端直接连接 mqtt.local:1883,路由器自动解析。
优点 :零代码改动,最稳定
缺点:需要路由器权限,换路由器失效
方案二:mDNS / Bonjour / Avahi(零配置,推荐)
无需路由器支持,设备自广播域名。
安卓端(作为 Broker,广播自己的域名)
kotlin
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
class MdnsBrokerAdvertiser(context: Context) {
private val nsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
fun advertise(port: Int) {
val serviceInfo = NsdServiceInfo().apply {
serviceName = "AndroidMQTT" // 设备看到的名字
serviceType = "_mqtt._tcp." // 服务类型
this.port = port
setAttribute("path", "/")
}
nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD,
object : NsdManager.RegistrationListener {
override fun onServiceRegistered(info: NsdServiceInfo) {
Log.d("mDNS", "已注册: ${info.serviceName}._mqtt._tcp.local")
}
// ... 其他回调
})
}
}
设备端连接 AndroidMQTT._mqtt._tcp.local 或 AndroidMQTT.local。
设备端(ESP32 + mDNS)
cpp
#include <ESPmDNS.h>
void setup() {
WiFi.begin("SSID", "PASSWORD");
// 注册自己的 mDNS
if (MDNS.begin("device001")) {
MDNS.addService("mqtt", "tcp", 1883);
}
// 发现 Broker
int n = MDNS.queryService("mqtt", "tcp");
for (int i = 0; i < n; i++) {
Serial.print("Broker: ");
Serial.print(MDNS.hostname(i)); // AndroidMQTT
Serial.print(" @ ");
Serial.print(MDNS.IP(i)); // 192.168.1.100
Serial.print(":");
Serial.println(MDNS.port(i)); // 1883
mqttClient.setServer(MDNS.IP(i), MDNS.port(i));
}
}
方案三:自建局域网 DNS 服务器(企业/高级场景)
在局域网内跑一个轻量级 DNS(如 dnsmasq、Pi-hole):
bash
# dnsmasq 配置 /etc/dnsmasq.conf
address=/mqtt.local/192.168.1.100
address=/broker.local/192.168.1.100
所有设备 DNS 指向这个服务器,即可解析 mqtt.local。
优点 :集中管理,灵活
缺点:需要额外设备/服务器
方案四:本地 Hosts 文件(仅限开发调试)
安卓端(需要root): /system/etc/hosts
设备端: 不支持(嵌入式系统通常无hosts概念)
不适用生产环境。
方案对比
| 方案 | 需要额外设备 | 需要路由器权限 | 跨平台支持 | 自动更新IP | 推荐度 |
|---|---|---|---|---|---|
| 路由器DNS绑定 | ❌ | ✅ | ✅ | ❌(需手动改) | ⭐⭐⭐ |
| mDNS/Bonjour | ❌ | ❌ | ✅ | ✅ | ⭐⭐⭐⭐⭐ |
| 自建DNS服务器 | ✅ | ❌ | ✅ | ✅ | ⭐⭐⭐⭐ |
| Hosts文件 | ❌ | ❌ | ❌ | ❌ | ⭐ |
为什么 mDNS 是最佳局域网域名方案
┌─────────────┐ mDNS广播 ┌─────────────┐
│ 安卓手机 │ ──► AndroidMQTT.local │ ESP32 │
│ 192.168.1.100│ ◄── device001.local │ 192.168.1.101│
│ (Broker) │ 互相发现 │ (设备) │
└─────────────┘ └─────────────┘
- 零配置:不需要路由器支持,不需要手动配IP
- 自发现:设备上线自动广播,下线自动消失
- IP变动自动处理:IP变了重新广播,客户端自动解析新IP
- 跨平台:安卓(NSD)、iOS(Bonjour)、Linux(Avahi)、ESP32(ESPmDNS) 都支持
实战代码:完整 mDNS 发现流程
安卓端(Broker + mDNS广播)
kotlin
class MqttBrokerService : Service() {
private lateinit var nsdManager: NsdManager
override fun onCreate() {
super.onCreate()
startMqttBroker()
advertiseMdns(1883)
}
private fun advertiseMdns(port: Int) {
nsdManager = getSystemService(Context.NSD_SERVICE) as NsdManager
val serviceInfo = NsdServiceInfo().apply {
serviceName = "AndroidMQTTBroker" // 设备连接用的名字
serviceType = "_mqtt._tcp."
this.port = port
}
nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD,
object : NsdManager.RegistrationListener {
override fun onServiceRegistered(info: NsdServiceInfo) {
Log.d("mDNS", "Broker域名: ${info.serviceName}._mqtt._tcp.local")
}
override fun onRegistrationFailed(info: NsdServiceInfo, errorCode: Int) {}
override fun onServiceUnregistered(info: NsdServiceInfo) {}
override fun onUnregistrationFailed(info: NsdServiceInfo, errorCode: Int) {}
})
}
}
设备端(ESP32 发现 + 连接)
cpp
#include <WiFi.h>
#include <ESPmDNS.h>
#include <PubSubClient.h>
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
bool discoverAndConnectBroker() {
// 搜索 _mqtt._tcp 服务
int n = MDNS.queryService("mqtt", "tcp");
if (n == 0) {
Serial.println("未发现Broker");
return false;
}
// 使用第一个发现的Broker
IPAddress brokerIp = MDNS.IP(0);
int brokerPort = MDNS.port(0);
Serial.print("发现Broker: ");
Serial.print(MDNS.hostname(0));
Serial.print(" @ ");
Serial.print(brokerIp);
Serial.print(":");
Serial.println(brokerPort);
mqtt.setServer(brokerIp, brokerPort);
if (mqtt.connect("device001")) {
Serial.println("MQTT连接成功");
return true;
}
return false;
}
void setup() {
Serial.begin(115200);
WiFi.begin("SSID", "PASSWORD");
while (WiFi.status() != WL_CONNECTED) delay(500);
// 启动mDNS(可选,设备自己也可以广播)
MDNS.begin("device001");
// 发现Broker并连接
while (!discoverAndConnectBroker()) {
delay(5000);
}
}
void loop() {
if (!mqtt.connected()) {
discoverAndConnectBroker(); // 断线重连时重新发现
}
mqtt.loop();
}
关键要点
| 问题 | mDNS解决方案 |
|---|---|
| IP变动 | 自动重新广播新IP,客户端重新解析 |
| 多Broker | 发现多个时按优先级/负载选择 |
| 无路由器权限 | 完全不依赖路由器 |
| 跨网段 | 同网段可用,跨网段需配合DNS-SD转发 |
mDNS 就是局域网里的"零配置DNS" ,让你的设备像访问 google.com 一样访问 AndroidMQTT.local,完全不需要关心底层IP是什么。