mDNS(Multicast DNS)是一种多播域名系统协议,它允许设备在局域网内通过名称解析互相发现,而无需依赖中央DNS服务器。以下是对mDNS的详细介绍:
工作原理
-
多播查询:设备发送DNS查询请求到多播地址224.0.0.251:5353
,请求解析局域网内的某个设备名称。
-
设备响应:拥有该名称的设备会响应查询,并提供自己的IP地址。
-
设备广播:设备启动时会广播自己的mDNS服务信息,包括设备名称、IP地址等。
-
定时更新:设备会定时重发mDNS广播消息,以保持设备间的连接状态。
协议特点
-
无需中央DNS服务器:mDNS不依赖于中央DNS服务器,设备可以在没有配置DNS服务器的情况下,通过主机名进行相互发现。
-
支持零配置网络:mDNS支持零配置网络(Zeroconf),设备无需手动配置就可以进行通信和发现。
-
多播通信:mDNS使用多播地址224.0.0.251
(IPv4)和FF02::FB
(IPv6),以及UDP端口5353
。
应用场景
-
智能家居:家庭智能设备通过mDNS广播自己的名字和服务信息,手机和电脑能够快速发现并与它们通信。
-
文件和媒体共享:在macOS或Linux系统中,设备通过mDNS发现局域网中的共享文件夹、媒体服务器等服务。
-
开发设备连接:树莓派等开发板通过mDNS注册名字,用户可以通过简单的名称(如raspberrypi.local
)进行访问。
-
打印机自动发现:通过mDNS协议,打印机可以广播自己的服务信息,使得用户可以方便地在网络中找到并连接打印机。
现实生活里,我们会接触到非常多使用mDNS的设备,例如摄像头、智能音箱、电视机 、打印机等等。如果是树莓派单板计算机,或者一些国产的单板计算机,一般都支持使用 主机名.local直接访问。像我之前经常刷的CircuitPython,也默认打开了mDNS功能,方便用户快速找到设备。
而esp idf的官方组件库里,也有mdns库。https://components.espressif.com/
第一个mdns就是了,然后我们可以在命令行使用 idf.py add-dependency "espressif/mdns^1.7.0" 这个指令把库添加到依赖里。
添加依赖之后,我们需要按下面步骤修改
1、添加网络协议头文件依赖,修改idf_component.yml文件,添加以下内容
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
添加这个之后,就不需要从idf目录里复制protocol_examples_common对应头文件了,这个头文件是esp idf很多使用了网络的案例用到的
2、从mDNS仓库里,复制Kconfig.projbuild目录过来,这个文件是配置mDNS相关的配置
3、复制案例文件到自己的源码文件里,这里懒得折腾了,直接复制到helloworld文件里。
复制完代码后,可以把多余的解析mDNS和注册多台设备的代码删除,然后打开menuconfig,配置好wifi信息
删除多余的代码后,其实可以看到整体的逻辑是非常简单的,初始化wifi后,就初始化mDNS功能,并注册对应的属性即可。
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/*
* MDNS-SD Query and advertise Example
*/
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "mdns.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE
#define EXAMPLE_BUTTON_GPIO CONFIG_MDNS_BUTTON_GPIO
static const char *TAG = "mdns-test";
static char *generate_hostname(void);
static void initialise_mdns(void)
{
char *hostname = generate_hostname();
//initialize mDNS
ESP_ERROR_CHECK( mdns_init() );
//set mDNS hostname (required if you want to advertise services)
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
//set default mDNS instance name
ESP_ERROR_CHECK( mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE) );
//structure with TXT records
mdns_txt_item_t serviceTxtData[3] = {
{"board", "esp32"},
{"u", "user"},
{"p", "password"}
};
//initialize service
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebSocket", "_http", "_tcp", 80, serviceTxtData, 3) );
//add another TXT item
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") );
//change TXT item value
ESP_ERROR_CHECK( mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")) );
free(hostname);
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
initialise_mdns();
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
#if defined(CONFIG_MDNS_ADD_CUSTOM_NETIF) && !defined(CONFIG_MDNS_PREDEF_NETIF_STA) && !defined(CONFIG_MDNS_PREDEF_NETIF_ETH)
/* Demonstration of adding a custom netif to mdns service, but we're adding the default example one,
* so we must disable all predefined interfaces (PREDEF_NETIF_STA, AP and ETH) first
*/
ESP_ERROR_CHECK(mdns_register_netif(EXAMPLE_INTERFACE));
/* It is not enough to just register the interface, we have to enable is manually.
* This is typically performed in "GOT_IP" event handler, but we call it here directly
* since the `EXAMPLE_INTERFACE` netif is connected already, to keep the example simple.
*/
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ENABLE_IP4 | MDNS_EVENT_ENABLE_IP6));
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ANNOUNCE_IP4 | MDNS_EVENT_ANNOUNCE_IP6));
#endif // CONFIG_MDNS_ADD_CUSTOM_NETIF
}
/** Generate host name based on sdkconfig, optionally adding a portion of MAC address to it.
* [url=home.php?mod=space&uid=784970]@return[/url] host name string allocated from the heap
*/
static char *generate_hostname(void)
{
#ifndef CONFIG_MDNS_ADD_MAC_TO_HOSTNAME
return strdup(CONFIG_MDNS_HOSTNAME);
#else
uint8_t mac[6];
char *hostname;
esp_read_mac(mac, ESP_MAC_WIFI_STA);
if (-1 == asprintf(&hostname, "%s-%02X%02X%02X", CONFIG_MDNS_HOSTNAME, mac[3], mac[4], mac[5])) {
abort();
}
return hostname;
#endif
}
如下图,修改好wifi名称和密码,其他默认也可以了
然后编译好,下载到板子上,运行。这时候找一个可以搜索mDNS设备的程序,例如下图的mDNS浏览器,就可以搜索到设备了
点击Details按钮后,还可以看到mDNS服务的其他特性,其实我们使用mDNS,就是可以通过主机.local直接访问设备,也可以使用_http._tcp或者其他特性描述来搜索相关的设备。
假如网络里有多个设备,我们可以将设备名称添加上设备号或者其他标识符,然后还可以通过txt属性附带的文本来区别设备,或者提供其他的一些信息。
总结:mDNS功能还是非常强大的,有兴趣的话,大家可以自行尝试一下。