ESP8266開發之旅 網絡篇⑫ 域名服務——ESP8266mDNS庫

1. 前言

    前面的博文中,不管是做爲client端仍是server端,它們之間的通訊都是經過具體的IP地址來尋址。經過IP地址來尋址,自己就是一個弊端,用戶怎麼會去記住這些魔法數字呢?那麼有沒有辦法能夠經過其餘方式來映射到IP地址,咱們只須要記住有意義的名字呢?
    通常來講,咱們遇到問題,不少同窗包括我本身,都很喜歡去baidu或者google,那麼baidu或者google是怎麼映射到IP地址呢?其實,這裏就用到了一種叫作別名的技術,DNS服務。
    DNS(Domain Name System,域名系統),因特網上做爲域名和IP地址相互映射的一個分佈式數據庫,可以使用戶更方便的訪問互聯網,而不用去記住可以被機器直接讀取的IP數串。經過主機名,最終獲得該主機名對應的IP地址的過程叫作域名解析(或主機名解析)。html

  • DNS協議運行在UDP協議之上,使用端口號53。
  • 關於UDP通訊,請回顧博主 ESP8266開發之旅 網絡篇⑩ UDP服務,重點理解UDP廣播;

    可是,博主本次講的並非DNS服務,而是以它來引入域名和IP地址相互映射的概念,而且引入本帖子的重點內容 ESP8266mDNS庫。
    其中,ESP8266mDNS是採用mDNS協議,跟DNS服務是兩種不一樣的概念,請不要混淆。web

2. mDNS詳解

2.1 mDNS協議

    mDNS,便是組播dns(Multicast DNS),mDNS主要實現了在沒有傳統DNS服務器的狀況下使局域網內的主機實現本地發現和域名訪問,使用端口爲5353,聽從dns協議,使用現有的DNS信息結構、名語法和資源記錄類型,而且沒有指定新的操做代碼或者響應代碼。
    這裏須要注意幾個點:數據庫

  1. 沒有傳統DNS服務器
  2. 局域網本地發現和域名發現
  3. 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提供的服務要遠遠多於這個,固然服務多但並不複雜。app

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地址以及端口號。tcp

注意點:分佈式

  • 思考是否局域網會出現一樣名字的服務?(局域網內部不能有重名的host或者service)

    ESP8266使用mDNS服務,請在代碼中加入如下頭文件:函數

#include <ESP8266mDNS.h>

3. ESP8266mDNS庫

    mDNS用到了ESP8266mDNS庫,因此接下來咱們來了解一下這個庫。使用這個庫的時候ESP8266能夠在AP模式或是以STA模式接入局域網。局域網中的其餘開啓mDNS服務的設備就能夠經過網址訪問ESP8266了;oop

  • Windowx系統下,通常都是沒有安裝mDNS服務的,讀者能夠安裝 Bonjour window,這是由蘋果公司爲基於組播域名服務(multicast DNS)的開放性零配置網絡標準所起的名字。Bonjour使得局域網中的系統和服務即便在沒有網絡管理員的狀況下也很容易被找到。Bonjour技術在Mac OS以及Itunes、Iphone上都獲得了普遍應用。
  • 蘋果系統,默認有mDNS服務;
  • 安卓系統手機,也自帶了mDNS服務,它是一個名爲mdnsd的程序;

因此你們基本上能夠放心使用mDNS服務,基本上的大平臺都支持了。

    老規矩,先上一個博主總結的百度腦圖:
image

整體上,根據功能能夠把方法分爲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。

實例準備

實例源碼

/**
 * 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");
}

實例結果:
image

image

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庫的使用
相關文章
相關標籤/搜索