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是什么。

相关推荐
2401_8685347813 小时前
NFV:将安全设备部署到虚拟机上
网络
zhengfei61113 小时前
【渗透工具】Payloader — 渗透测试辅助平台(payload一键所有)
网络·安全·web安全
鼎讯信通14 小时前
风电光缆运维提质增效:G-4000A 光缆故障追踪仪破解风场巡检难题
运维·网络·数据库
Multipath71215 小时前
无人区不掉线:多链路聚合路由,为环塔拉力赛筑起“空中通讯走廊”
网络·5g·安全·无人机·实时音视频
上海云盾-小余17 小时前
接口高频恶意刷取怎么防?网关限流搭配 WAF 联合防护方案
网络·安全
潜创微科技17 小时前
4K60 over IP 方案简介
网络·嵌入式硬件·网络协议·tcp/ip·音视频
treesforest17 小时前
自媒体账号限流排查指南:从风控算法视角看IP纯净度与网络隔离
网络·tcp/ip·ip·媒体
pride.li17 小时前
海思视觉Hi3516CV610--开机自动设置ip
linux·网络·网络协议·tcp/ip
AskHarries19 小时前
权限模型:Shell、Browser、文件读写的安全边界
服务器·前端·网络
咖啡星人k19 小时前
MonkeyCode 网络架构:WebSocket、SSE与实时协作的技术选型
网络·websocket·架构·monkeycode