mDNS 就是局域网里的“零配置DNS“

局域网域名方案的实现方式

方案一:路由器内置 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.localAndroidMQTT.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)    │         互相发现           │  (设备)      │
└─────────────┘                          └─────────────┘
  1. 零配置:不需要路由器支持,不需要手动配IP
  2. 自发现:设备上线自动广播,下线自动消失
  3. IP变动自动处理:IP变了重新广播,客户端自动解析新IP
  4. 跨平台:安卓(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是什么。

相关推荐
Hello_Embed2 小时前
嵌入式上位机开发入门(二十九):JsonRPC TCP Server
网络·单片机·网络协议·tcp/ip·json·嵌入式
南境十里·墨染春水2 小时前
linux学习进展 网络基础
linux·网络·学习
Rust研习社2 小时前
Reqwest 兼顾简洁与高性能的现代 HTTP 客户端
开发语言·网络·后端·http·rust
大熊背2 小时前
ISP Pipeline中Lv实现方式探究之六--lv值计算再优化
网络·算法·自动曝光·lv
RTC老炮2 小时前
WebRTC下FlexFEC算法架构及原理
网络·算法·音视频·webrtc
格林威2 小时前
面阵相机 vs 线阵相机:堡盟与Basler选型差异全解析 + Python实战演示
开发语言·网络·人工智能·python·数码相机·yolo·工业相机
深蓝海拓2 小时前
基于QtPy (PySide6) 的PLC-HMI工程项目(十二)最后的工作
网络·笔记·python·学习·pyqt·plc
7yewh2 小时前
针对灵巧手机械结构的探究
网络·人工智能·单片机·深度学习·嵌入式
S1998_1997111609•X2 小时前
HTTPS/:~/:-go、f\i=.kill/all,app==-nm\Ā=black,IP.apm
网络