6

我正在构建一个 IoT 解决方案,我将在其中将多个设备连接到我想要连接到控制我的解决方案的集线器的本地网络。

连接新设备后,我想将其与集线器连接并为其命名以识别它。我想以自动方式实现检测(所以我不必手动输入 IP 地址)。例如,当网络中存在 Chromecast 时,我可以在手机的流媒体应用程序中看到它。所以我想做一些类似的事情来将集线器与设备连接起来。

到目前为止,我的想法是有两种方法:

  1. 集线器扫描网络以查找新设备(定期或当我说存在新设备时)。
  2. 一旦连接,设备就会扫描网络以找到集线器。

这些方法中的任何一种都比另一种更喜欢吗?在这种情况下,为什么?

在进行扫描时,无论我选择什么方向,最有效的扫描方式是什么?我正在使用 Java 进行实现,到目前为止我所拥有的是:

int timeout = 100;
for (int i = 1; i < 255; i++)
{
    String host = subnet + "." + i;

    if (InetAddress.getByName(host).isReachable(timeout))
    {
        String hostname = InetAddress.getByName(host).getHostName();
        String canonicalHostName = InetAddress.getByName(host).getCanonicalHostName();
        System.out.println(host + " is reachable. Hostname: " + hostname + ", CanonicalHostName: " + canonicalHostName);
    }
}

我在这里看到的是,对于我网络中的大多数内容,返回的主机名只是 IP 地址,而不是我在路由器中看到的主机名。我想我可以使用主机名作为识别器来检测特定设备并了解它们在哪里 - 但是这个小 poc 似乎不起作用。那么如何才能很好地识别设备呢?

Java(或Javascript)和ESP8266是否有任何库/解决方案可以做到这一点?(如果实施“智能家居”设备,感觉就像一个常见问题)。

4

2 回答 2

7

我们没有单一的方法可以发现局域网上的设备。

设备通常不是扫描网络,而是使用多播或广播协议来宣布它们的存在,或者它们在(通常是外部的)预先配置的服务器上会合。

一些设备使用mDNS - 松散地基于 DNS 协议,它们多播数据包以通告它们在网络上的存在。Apple 在其产品中使用 mDNS。它在 Android 上的支持很差,并且需要在 Windows 上安装额外的软件。mDNS 名称通常在.local域中。

一些设备使用UPNPSSDP - Belkin 的 Wemo 系列产品可以做到这一点。UPNP 和 SSDP 是基于 XML 和SOAP的过于复杂的协议,对于 RAM 和处理能力有限的设备(如 ESP8266 和 ESP32)来说,它们不是很好的选择。

有些设备只是推出自己的协议。Haiku 的“Big Ass Fan”系列就是这样做的——他们使用本土协议广播 UDP 数据包,该协议至少在最初容易受到各种问题的影响。除非您真的知道自己在做什么,否则我不建议您走这条路。其他已建立的协议已经有机会消除这些错误。除非您在协议设计方面经验丰富,否则您更有可能重新发明其他协议所遇到的问题,而不是一个美妙的闪亮的新可发现性协议。

这些类型的设备将定期广播或多播一个数据包来宣布自己,或者您称为“集线器”的东西将广播或多播请求,并且设备将响应该请求。

并非所有设备都提供直接控制它们所连接的 LAN 的接口。有些只是与远程服务器会合。在这种情况下,您通过要求服务器枚举它们来发现它们,并通过该服务器控制它们。Google 的 Nest 产品就是这样工作的——初始配置是通过蓝牙完成的;之后,应用程序通过远程服务器与设备通信。

以这种方式集合的设备通常预先配置有集合服务器的名称,但它们也可能在网络配置期间配置有服务器的名称(通常应用程序与它们通信以共享 wifi 凭据;它还可能共享有关会合服务器)。

我们通常不会扫描 IP 地址块中的名称或主动探测新设备,除非我们正在调试网络或进行某种安全扫描。

您描述的扫描 IP 地址块的过程存在问题且不可靠。它起作用的唯一原因是某些路由器从设备的 DHCP 请求中获取设备的名称(或者路由器可能被配置为知道设备的名称)。路由器还为网络上的设备处理 DNS,通常通过将它们转发到 ISP 的 DNS 服务器或网络所有者配置它使用的 DNS 服务器。它拦截对它知道名称的设备的 DNS 请求并自行回复它们,而不是将它们转发到外部 DNS 服务器。

您还必须了解网络配置才能正确执行此操作。网络只有/24吗?如果是 /22 怎么办?还是 /16?如果网络配置为 /8,您准备好扫描 2^24 个 IP 地址吗?

尽管路由器可能会拦截名称和返回地址的请求,但它不一定会拦截地址名称并返回名称。

扫描还会产生不必要的网络流量。虽然您的一个“集线器”扫描可能看起来不多,但如果您运行来自不同制造商的多台扫描仪,它的扩展性就不会很好。

如果您的“集线器”绕过路由器进行 DNS 请求,那么它也将无法解析路由器提供的名称。

也不是所有的路由器都这样做。它不是 Internet 架构的一部分,它是一些路由器提供的便利功能。你不能依赖它工作。

您还可以尝试主动扫描网络,尝试与网络上的每个 IP 地址进行通信。我们这样做是为了进行网络调试,但不断运行它以检测新设备将是一种与网络交互的敌对方式。

网络基础设施设备——交换机和路由器——当然会维护网络上所有活动设备的列表。您可以使用SNMP(简单网络管理协议)访问这些列表,但很少有消费者交换机和路由器支持 SNMP。允许随机访问您的网络基础设施是一场网络安全噩梦。

您最好的选择是一个简单的多播/广播协议,如 mDNS。不要扫描、宣布或请求。它响应迅速,不会给网络带来负担,不依赖于路由器的特性,并且不会让网络管理员讨厌你。

于 2020-11-23T19:51:50.407 回答
1

很久以前,我实现了一个类似的解决方案来发现连接到网络的客户端。我的策略包括利用 DHCP 配置。

如果您的设备必须从您控制的 Linux/Unix DHCP 服务器请求 IP 地址,您可以将其配置为在设备连接或断开网络时通知您。

我们使用的是 Linux DHCP 服务器,在dhcpd.conf中有一个关于事件的部分。基本上它说,当 DHCP 为客户端提交特定的 IP 地址租约时,它会引发一个事件,当这种情况发生时,它可以运行您可以为该事件定义的侦听器。在侦听器中,我们可以要求 DHCP 执行命令,例如将客户端信息(例如 MAC 地址)及其 IP 地址租约写入命名管道,而您的 Java 应用程序可能只是从该队列中读取。(有关详细信息,另请参阅dhcp-eval)。当客户端释放 IP 租约或租约到期时,可以运行类似的事件。

这是一个例子

subnet 192.168.1.0 netmask 255.255.255.0 {
    option routers  192.168.1.2;

    on commit {
        set clip = binary-to-ascii(10, 8, ".", leased-address);
        set clhw = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
        execute("/usr/local/sbin/dhcpevent", "commit", clip, clhw, host-decl-name);
    }
    ...

如果您可以控制您的 DHCP 配置,那么您可以从中提取很多内容。

于 2020-11-30T12:26:31.447 回答