ESP8266开发之旅 网络篇⑫ 域名服务——ESP8266mDNS库
1. 前言
前面的博文中,无论是作为client端还是server端,它们之间的通信都是通过具体的IP地址来寻址。通过IP地址来寻址,本身就是一个弊端,用户怎么会去记住这些魔法数字呢?那么有没有办法可以通过其他方式来映射到IP地址,我们只需要记住有意义的名字呢?
一般来说,我们遇到问题,很多同学包括我自己,都很喜欢去baidu或者google,那么baidu或者google是怎么映射到IP地址呢?其实,这里就用到了一种叫做别名的技术,DNS服务。
DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。
- DNS协议运行在UDP协议之上,使用端口号53。
- 关于UDP通信,请回顾博主 ESP8266开发之旅 网络篇⑩ UDP服务,重点理解UDP广播;
但是,博主本次讲的并不是DNS服务,而是以它来引入域名和IP地址相互映射的概念,并且引入本帖子的重点内容 ESP8266mDNS库。
其中,ESP8266mDNS是采用mDNS协议,跟DNS服务是两种不同的概念,请不要混淆。
2. mDNS详解
2.1 mDNS协议
mDNS,即是组播dns(Multicast DNS),mDNS主要实现了在没有传统DNS服务器的情况下使局域网内的主机实现本地发现和域名访问,使用端口为5353,遵从dns协议,使用现有的DNS信息结构、名语法和资源记录类型,并且没有指定新的操作代码或者响应代码。
这里需要注意几个点:
- 没有传统DNS服务器;
- 局域网本地发现和域名发现;
- mDNS基于UDP协议,确切来说,运用了UDP广播;
在局域网中,设备和设备之间相互通信需要知道对方的具体IP地址。而通常情况下,如果我们不去设置静态ip地址,一般都是通过DHCP client动态分配IP地址,这个时候如果没有可视化界面去查看,一般我们是无法得知具体的IP地址,我们也就无法与之通信。这个时候,就是mDNS大显神威的时候,我们通过具体的名字去访问就好。
2.2 组播地址
mdns 使用组播地址为: 224.0.0.251 (ipv6: FF02::FB) 端口为5353,mdns 是用于局域网内部的,并且主机的域名为.local 结尾;
2.3 工作原理
每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,我是谁,和我的IP地址是多少。然后其他也开启了mDNS服务的主机就会响应,也会告诉你,它是谁,它的IP地址是多少。当然,具体实现要比这个复杂点。
比如,A主机进入局域网,开启了 mDNS 服务,并向 mDNS 服务注册以下信息:我提供 FTP 服务,我的IP是 192.168.1.101,端口是 21。当B主机进入局域网,并向 B 主机的 mDNS 服务请求,我要找局域网内 FTP 服务器,B主机的 mDNS 就会去局域网内向其他的 mDNS 询问,并且最终告诉你,有一个IP地址为 192.168.1.101,端口号是 21 的主机,也就是 A 主机提供 FTP 服务,所以 B 主机就知道了 A 主机的 IP 地址和端口号了。
大概的原理就是这样子,mDNS提供的服务要远远多于这个,当然服务多但并不复杂。
2.4 mDNS Service Discovery(mDNS-SD)
在前面,我们说到开启了mDNS服务的主机在进入局域网后,会向局域网内的所有主机组播一个消息,我是谁,我的IP地址是多少,然后其他也开启了mDNS服务的主机也会响应告诉你它是谁,ip地址是多少。在ESP8266mDNS库中,除了前面的mDNSResponder功能外,还包括了mDNS-SD(服务注册,服务查询,服务解析)功能,通过方法可以获取到局域网内具体的服务信息。
举个例子:在局域网内,要进行打印服务,必须要先知道打印服务器的IP地址。此IP地址一般由IT部门人员负责分配,然后他还得全公司发邮件来通知各个部门人员该IP地址。有了mDNS-SD服务,打印服务器注册一个打印服务,名为“print service”之类的。当有人需要打印服务时,一般会先搜索局域网的打印服务器。但是由于不知道打印服务器的IP地址,用户只能通过诸如“print service”的名字去查找打印机,在mDNS-SD的帮助下,用户最终能找到注册了“print service”名字的打印机,并获得它的IP地址以及端口号。
注意点:
- 思考是否局域网会出现同样名字的服务?(局域网内部不能有重名的host或者service)
ESP8266使用mDNS服务,请在代码中加入以下头文件:
#include <ESP8266mDNS.h>
3. ESP8266mDNS库
mDNS用到了ESP8266mDNS库,所以接下来我们来了解一下这个库。使用这个库的时候ESP8266可以在AP模式或是以STA模式接入局域网。局域网中的其他开启mDNS服务的设备就可以通过网址访问ESP8266了;
- Windowx系统下,一般都是没有安装mDNS服务的,读者可以安装 Bonjour window,这是由苹果公司为基于组播域名服务(multicast DNS)的开放性零配置网络标准所起的名字。Bonjour使得局域网中的系统和服务即使在没有网络管理员的情况下也很容易被找到。Bonjour技术在Mac OS以及Itunes、Iphone上都得到了广泛应用。
- 苹果系统,默认有mDNS服务;
- 安卓系统手机,也自带了mDNS服务,它是一个名为mdnsd的程序;
所以大家基本上可以放心使用mDNS服务,基本上的大平台都支持了。
老规矩,先上一个博主总结的百度脑图:

总体上,根据功能可以把方法分为2大类:
- 管理mDNS服务;
- 管理mDNS-SD服务;
3.1 管理mDNS服务
3.1.1 begin —— 开启mDNS服务
函数说明:
/**
* 启动mDNS服务
* @param hostName const char* (与IP地址映射的域名)
* @return bool 是否启动成功
*/
bool begin(const char* hostName);
3.1.2 notifyAPChange —— 当AP更新或者禁止后调用
函数说明:
/**
* 通知AP改变
*/
void notifyAPChange();
3.2 管理mDNS服务
3.2.1 addService —— 注册服务
函数说明:
/**
* 注册服务
* @param service 服务名字
* @param proto 服务协议
* @param port 服务端口
*/
void addService(char *service, char *proto, uint16_t port);
void addService(const char *service, const char *proto, uint16_t port);
3.2.2 queryService —— 查询服务
函数说明:
/**
* 查询服务
* @param service 服务名字
* @param proto 服务协议
* @return count 返回符合条件的服务个数
*/
int queryService(char *service, char *proto);
int queryService(const char *service, const char *proto);
int queryService(String service, String proto);
3.2.3 hostname —— 获取查询服务的主机名
函数说明:
/**
* 返回服务域名
* @param idx 服务索引
*/
String hostname(int idx);
3.2.4 IP —— 获取查询服务的IP地址
函数说明:
/**
* 返回服务IP地址
* @param idx 服务索引
*/
IPAddress IP(int idx);
3.2.5 port —— 获取查询服务的端口号
函数说明:
/**
* 返回服务端口
* @param idx 服务索引
*/
uint16_t port(int idx);
4. 实例代码
4.1 演示ESP8266 mDNS responder功能
实例说明:
演示ESP8266 mDNS responder功能,烧录以下代码到NodeMcu,然后在电脑端输入以下地址:
http://esp8266.local/
以域名方式来访问webserver。
实例准备:
- NodeMcu开发板
- 已经按照 Bonjour 服务
- 用到webserver,请参考 ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
实例源码:
/**
* Demo:
* 演示ESP8266 mDNS responder功能
* @author 单片机菜鸟
* @date 2019/01/28
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
const char* AP_SSID = "TP-LINK_5344"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* AP_PSK = "6206908you11011010"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const unsigned long BAUD_RATE = 115200;// serial connection speed
//声明一下函数
void initBasic(void);
void initWifi(void);
void initWebServer(void);
void initmDNS(void);
ESP8266WebServer server(80);
/**
* 处理根目录uri请求
* uri:http://server_ip/
*/
void handleRoot() {
DebugPrintln("handleRoot");
server.send(200, "text/html", "Hello From ESP8266 mDNS demo");
}
/**
* 处理无效uri
* uri:http://server_ip/xxxx
*/
void handleNotFound() {
DebugPrintln("handleNotFound");
//打印无效uri的信息 包括请求方式 请求参数
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup(void) {
initBasic();
initWifi();
initWebServer();
initmDNS();
}
void loop(void) {
server.handleClient();
}
/**
* 初始化基础功能:波特率
*/
void initBasic(){
DebugBegin(BAUD_RATE);
}
/**
* 初始化wifi模块:工作模式 连接网络
*/
void initWifi(){
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PSK);
DebugPrintln("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrint("Connected to ");
DebugPrintln(AP_SSID);
DebugPrint("IP address: ");
DebugPrintln(WiFi.localIP());
}
/**
* 初始化webserver
*/
void initWebServer(){
//以下配置uri对应的handler
server.on("/", handleRoot);
server.on("/inline", []() {
DebugPrintln("handleInline");
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
//启动webserver
server.begin();
DebugPrintln("HTTP server started");
}
/**
* 初始化mDNS
*/
void initmDNS(){
if (!MDNS.begin("esp8266")) {
DebugPrintln("Error setting up MDNS responder!");
while (1) {
delay(1000);
}
}
DebugPrintln("mDNS responder started,please input http://esp8266.local/ in your browser after install Bonjour");
}
实例结果:


4.2 演示ESP8266 mDNS 发现服务功能
实例说明:
演示ESP8266 mDNS 发现服务功能,分别烧录代码到两块NodeMcu。
实例准备:
- 两块NodeMcu开发板
实例源码:
/*
演示ESP8266 mDNS 发现服务功能
注意:
- 输入你的 WiFi SSID 和 password.
- 烧写到两块 ESP8266 板子
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
const char* ssid = "...";
const char* password = "...";
char hostString[16] = {0};
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("\r\nsetup()");
sprintf(hostString, "ESP_%06X", ESP.getChipId());
Serial.print("Hostname: ");
Serial.println(hostString);
WiFi.hostname(hostString);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!MDNS.begin(hostString)) {
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
//往mDNS里面注册服务
MDNS.addService("esp", "tcp", 8080);
Serial.println("Sending mDNS query");
//查找服务
int n = MDNS.queryService("esp", "tcp"); // Send out query for esp tcp services
Serial.println("mDNS query done");
if (n == 0) {
Serial.println("no services found");
} else {
Serial.print(n);
Serial.println(" service(s) found");
for (int i = 0; i < n; ++i) {
// 打印查找到的服务具体信息
Serial.print(i + 1);
Serial.print(": ");
Serial.print(MDNS.hostname(i));
Serial.print(" (");
Serial.print(MDNS.IP(i));
Serial.print(":");
Serial.print(MDNS.port(i));
Serial.println(")");
}
}
Serial.println();
Serial.println("loop() next");
}
void loop() {
// put your main code here, to run repeatedly:
}
5. 总结
本节主要讲解mDNS在8266上的域名映射应用,整体上内容不多,但是是一个非常有用的知识点,希望读者能仔细学习。并且可以的话,回顾一下以下两个篇章:
- ESP8266开发之旅 网络篇⑩ UDP服务
- ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
ESP8266开发之旅 网络篇⑫ 域名服务——ESP8266mDNS库的更多相关文章
- ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用
1. 前言 在前面的篇章中,博主给大家讲解了ESP8266的软硬件配置以及基本功能使用,目的就是想让大家有个初步认识.并且,博主一直重点强调 ESP8266 WiFi模块有三种工作模式: St ...
- ESP8266开发之旅 网络篇④ Station——ESP8266WiFiSTA库的使用
1. 前言 在前面的篇章中,博主给大家讲解了ESP8266的软硬件配置以及基本功能使用,目的就是想让大家有个初步认识.并且,博主一直重点强调 ESP8266 WiFi模块有三种工作模式: St ...
- ESP8266开发之旅 网络篇⑥ ESP8266WiFiGeneric——基础库
1. 前言 在前面的博文中,博主介绍到ESP8266WiFi库是包含了很多功能的一个超级库.ESP8266WiFi库不仅仅局限于 ESP8266WiFi.h 和 ESP8266WiFi.cpp ...
- ESP8266开发之旅 网络篇⑮ DNSServer——真正的域名服务
1. 前言 Arduino for esp8266中有两个DNS服务相关的库: ESP8266mDNS库 这个库是mDNS库,使用这个库的时候ESP8266可以在AP模式或是以STA模式接入局 ...
- ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
随机推荐
- 实现一个正则表达式引擎in Python(一)
前言 项目地址:Regex in Python 开学摸鱼了几个礼拜,最近几天用Python造了一个正则表达式引擎的轮子,在这里记录分享一下. 实现目标 实现了所有基本语法 st = 'AS342abc ...
- [Pandas] 04 - Efficient I/O
SQLITE3接口 调动 SQLITE3数据库 import sqlite3 as sq3 query = 'CREATE TABLE numbs (Date date, No1 real, No2 ...
- Basler acA2500-14gm 连接采图报错处理
新购 Basler acA2500-14gm 相机,使用从官方网站下载的 pylon v6 版本连接测试,报错: Type Time Source MessageError 2 ...
- java工具方法
仅记录所遇到并使用的工具方法. 1.md5加密 /** * 对传入的字符串数据进行MD5加密 * @param source 字符串数据 * @param code 字符编码 * @return 加密 ...
- 转:查看oracle数据库允许的最大连接数和当前连接数
在查看数据的连接情况很有用,写完程序一边测试代码一边查看数据库连接的释放情况有助于分析优化出一个健壮的系统程序来. 1.查看当前的数据库连接数 select count(*) from v$proce ...
- 使用Python3.6的标准GUI库tkinter快速创建GUI应用程序
Python 提供了多个图形开发界面的库,几个常用 Python GUI 库如下: Tkinter: Tkinter 模块(Tk 接口)是 Python 的标准 Tk GUI 工具包的接口 .Tk 和 ...
- jstl.jar下载地址
下载地址: 链接:https://pan.baidu.com/s/15_B1QLelWOvTGdC7BoAp4A 密码:vmdr
- pt-archiver归档数据 源库和目标库是否会出现不一致
背景 归档的表在源库和目标库都要存在 pt-archiver归档表的场景有:不删原表数据,非批量插入目标库:不删原表数据,批量插入目标库:非批量删除原表数据,非批量插入目标库:批量删除原表数据,批量插 ...
- 【IT技术概念】WebAPI与传统的WebService有哪些不同?
在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下,你有很多的选择来构建一个HTTP Services.我分享一下我对 ...
- 利用threading模块开线程
一多线程的概念介绍 threading模块介绍 threading模块和multiprocessing模块在使用层面,有很大的相似性. 二.开启多线程的两种方式 1.创建线程的开销比创建进程的开销小, ...