ESP8266 - 使用本地 mDNS 地址时 HTTPClient 不工作

ESP8266 - HTTPClient not working when using local mDNS address

我的本地网络服务器有 mDNS - http://android.local:8182 我想通过使用 ESP8266HTTPClient 库中的 HTTPClient class 将它与 ESP8266 一起使用。代码如下:

HTTPClient http;
http.begin("http://android.local:8182");
http.addHeader("values.json", "text/json");
int code = http.POST(jsonStr);
Log(DATA, "HTTP code: " + String(code));
Log(DATA, "Response: " + http.getString());

但它总是returns -1 代码。当我从字面上复制粘贴 url 到我的电脑网络浏览器时,它起作用了。我尝试用我的服务器的 IP 地址替换 "android.local" 并且它有效,但不是 mDNS 地址。谁能告诉我如何让它正常工作?

Windows、macOS 和 Linux 都 运行 在比 ESP8266 更强大的计算机上。虽然常规 DNS 和 mDNS 几乎是相同的协议,但它们的使用方式不同并且分别实现。事实上,Windows和Linux在不安装额外软件的情况下不支持mDNS是很常见的(Windows下的"Bonjour"和Linux下的"avahi") .

您可以通过两种方式在 ESP8266 上使用 Arduino SDK 实现这一点。

无论哪种方式,您都需要将 android.local 解析为其 IP 地址,然后重写 URL 以包含 IP 地址而不是主机名。

不幸的是,ESP8266 mDNS 库不支持直接将主机名解析为 IP 地址,因此这会有点扭曲。

第一种方式:如果正在做广告的设备android.local提供了"http"、"tcp"的mDNS服务记录,并提供了IP地址和端口号(8182) advertisement,然后可以使用ESP8266mDNS库查询服务记录。

您的代码将如下所示:

// your other necessary #include's first
#include <ESP8266mDNS.h>

#define HOSTNAME "the name of this ESP8266"
#define TARGET_HOSTNAME "android"

bool resolve_mdns_service(char* service_name, char* protocol, char* desired_host, IPAddress* ip_addr, uint16_t *port_number) {
  Serial.println("Sending mDNS query");
  int n = MDNS.queryService(service_name, protocol);
  Serial.printf("mDNS query got %d results\n", n);

  if(n == 0) {
    Serial.println("no services found");
  } else {
    for (int i = 0; i < n; ++i) {
#ifdef DEBUG
      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(")");
#endif

      if(strcmp(MDNS.hostname(i).c_str(), desired_host) == 0) {
        *ip_addr = MDNS.IP(i);
        *port_number = MDNS.port(i);
        return true;
      }
    }
  }

  return false;
}

void setup() {
  // get WiFi setup first

  if(!MDNS.begin(HOSTNAME))
    Serial.println("Error setting up MDNS responder!");
  else
    Serial.println("mDNS responder started");

  IPAddress server_ip;
  uint16_t port_number;

  if(resolve_mdns_service("http", "tcp", TARGET_HOSTNAME, &server_ip, &port_number)) {
    Serial.printf("got an answer for %s.local!\n", TARGET_HOSTNAME);
    Serial.println(server_ip);
    Serial.println(port_number);
  } else {
    Serial.printf("Sorry, %s.local not found\n", TARGET_HOSTNAME);
  }
}

这仅在您的设备通告 "http"、"tcp" mDNS 服务记录时有效。

如果没有,最好的办法是使用第三方库。

在这种情况下,您需要按照说明在您的项目中安装此库:

https://github.com/madpilot/mDNSResolver

您的代码将如下所示:

// your other necessary #include's first
#include <mDNSResolver.h>

#define TARGET_HOSTNAME "android"

WiFiUDP udp;
mDNSResolver::Resolver resolver(udp);

void setup() {
  // get WiFi setup first

  Serial.printf("Attempting to resolve %s\n", TARGET_HOSTNAME);
  IPAddress answer = resolver.search(TARGET_HOSTNAME);
  if(answer == IPADDR_NONE) {
    Serial.println("no answer found");
  } else {
    Serial.println("Gotta result!");
    Serial.println(answer);
  }
}

第一种方法需要更多代码,但不需要外部第 3 方库,并且并非在所有情况下都有效。

获得 IP 地址后,您可以使用它来组合 URL 您传递给 http.begin() 并从那里继续。

mDNS 是一个 "unreliable" 协议 - 它不保证您会得到答案,或所有答案。因此,如果您在第一种方法中定义 DEBUG,则每次执行时您可能会看到略有不同的结果集。如果您的代码无法解析主机名,您可能需要在放弃之前重试 2-3 次。