当前位置: 首页 > article >正文

Android中使用NSD扫描,实现局域网内设备IP的发现

0. 前言

本文介绍了什么是NSD协议,并介绍了如何在Android中实现NSD的服务端和客户端,实现局域网内的设备发现功能。

1. NSD是什么

在Android开发中,NSD(Network Service Discovery)是一种用于在局域网内发现其他设备提供的网络服务的技术,NSD是Android SDK中自带的类库。

NSD扫描时,Android设备会在局域网内主动搜索已经通过NSD注册的服务。这些服务可能由打印机、摄像头、HTTPS服务器或其他移动设备提供。通过NSD扫描,Android应用可以获取到这些服务的名称、端口号和IP地址,从而为后续的网络通信或连接做准备。

在这里插入图片描述

2. NSD扫描的应用场景

NSD扫描在Android开发中有着广泛的应用场景,包括但不限于:

  • 局域网设备互联:通过NSD扫描,Android设备可以发现局域网内的其他设备,并实现设备间的互联和通信。
  • 发现网络打印机:在办公室或家庭环境中,Android设备可以通过NSD扫描发现网络打印机,并进行打印操作。
  • 多人游戏:在多人游戏场景中,NSD扫描可以帮助玩家发现同一局域网内的其他玩家设备,从而实现多人在线游戏。
  • 智能家居:在智能家居领域,NSD扫描可以用于发现和控制局域网内的智能家居设备,如智能灯泡、智能插座等。

3. NSD协议的原理

NSD协议即网络服务发现协议,其工作原理是基于DNS-SD (Domain Name System - Service Discovery)协议的服务发现机制,用于在网络中自动发现可用的服务和设备。

  • 基于DNS-SD的服务注册:设备作为服务提供者,会将自身提供的服务信息按照DNS-SD的格式要求,在DNS服务器上进行注册。这些信息包括服务类型、服务名称以及服务所在的主机地址等 。例如,一个提供打印服务的设备,会将打印服务的相关信息注册到DNS服务器,其中服务类型可能是“_http._tcp”,服务名称可以自定义为“打印机1”,同时还会注册该设备在网络中的IP地址。
  • 基于DNS-SD的服务查询:当设备作为服务使用者需要查找特定服务时,会向DNS服务器发送基于DNS-SD的查询请求。查询请求中会指定要查找的服务类型等信息,DNS服务器收到请求后,会根据注册的信息进行匹配查找,并将符合条件的服务实例信息返回给查询者。
  • 利用DNS基础设施:DNS-SD充分利用了现有的DNS基础设施来实现服务发现功能,无需额外构建复杂的网络架构和通信协议。这使得NSD协议能够在各种网络环境中较为便捷地部署和应用,只要网络中存在可用的DNS服务器,并且支持DNS-SD的相关记录类型,就可以实现服务的发布和发现。

4. NSD协议只能发现Android设备吗

NSD协议是Android系统中提供的一种网络服务发现机制,它允许应用在同一局域网内自动发现其他设备提供的服务,并与之建立连接。

而根据上文,我们已经知道NSD协议是基于DNS-SD协议的,所以NSD协议不仅限于发现Android设备,而是包括所有支持NSD协议或兼容DNS-SD(基于DNS的服务发现)的设备。

5. Android中如何实现NSD协议

Android中实现NSD协议需要使用NSDManager这个类,它用来发现网络服务的API。
这个API支持三个主要的操作 : registerService、discoverServices和resolveService。
registerService用来注册NSD服务,discoverServices用来发现NSD服务,resolveService用来解析NSD服务。

5.1 服务端的实现

5.1.1 添加权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
5.1.2 获取 NsdManager 管理类
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
//...

NsdManager mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
5.1.3 注册服务

首先会创建NsdServiceInfo对象来描述服务信息,包括服务名称(setServiceName)、服务类型(setServiceType)和端口号(setPort)。然后通过mNsdManager.registerService方法来注册服务,RegistrationListener用于接收注册过程中的各种事件。

private fun registerService() {
    val serviceInfo = NsdServiceInfo()
    serviceInfo.serviceName = "MyService"
    serviceInfo.serviceType = "_http._tcp."
    //实际项目开发中,端口号不应该写死,应该动态获取端口号,防止端口号已经被占用
    serviceInfo.port = 8586 
    mNsdManager.registerService(
        serviceInfo,
        NsdManager.PROTOCOL_DNS_SD,
        registrationListener
    )
}

private val registrationListener = object : RegistrationListener {
    override fun onRegistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // 注册服务失败时调用
    }

    override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // 注销服务失败时调用
    }

    override fun onServiceRegistered(serviceInfo: NsdServiceInfo) {
        // 服务注册成功时调用
    }

    override fun onServiceUnregistered(serviceInfo: NsdServiceInfo) {
        // 服务注销成功时调用
    }
}
5.1.3.1 动态获取端口号

实际项目开发中,端口号不应该写死,应该动态获取端口号,防止端口号已经被占用

import java.io.IOException;
import java.net.ServerSocket;

public class PortFinder {
    public static int getAvailablePort() {
        try {
            ServerSocket serverSocket = new ServerSocket(0);
            int port = serverSocket.getLocalPort();
            serverSocket.close();
            return port;
        } catch (IOException e) {
            e.printStackTrace();
            return -1;
        }
    }
}
5.1.4 停止发现服务

当不再需要发现服务时,可以使用mNsdManager.stopDiscovery方法来停止服务发现。

override fun onDestroy() {
    super.onDestroy()

    mNsdManager.unregisterService(registrationListener)
}

5.2 客户端的实现

5.2.1 添加权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
5.2.2 获取 NsdManager 管理类
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
//...

NsdManager mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
5.2.3 发现服务

可以使用NsdManager的discoverServices方法来发现服务。

private fun discoverServices() {
    mNsdManager.discoverServices(
        "_http._tcp.",
        NsdManager.PROTOCOL_DNS_SD,
        object : DiscoveryListener {
            override fun onDiscoveryStarted(regType: String) {
                // 当服务发现开始时调用
            }

            override fun onServiceFound(service: NsdServiceInfo) {
                // 当发现一个服务时调用
                mNsdManager.resolveService(service, object : NsdManager.ResolveListener {
                    override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
                        // 解析服务失败时调用
                    }

                    override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
                        // 服务解析成功时调用
                        val serviceName = serviceInfo.serviceName
                        val host = serviceInfo.host.hostAddress
                        val port = serviceInfo.port
                        // 在这里可以使用获取到的服务信息,例如连接到服务
                        Log.i("Heiko", "serviceName:$serviceName host:$host port:$port")
                        runOnUiThread {
                            Toast.makeText(this@MainActivity,"serviceName:$serviceName host:$host port:$port",Toast.LENGTH_SHORT).show()
                        }
                    }
                })
            }

            override fun onServiceLost(service: NsdServiceInfo) {
                // 当服务丢失时调用
            }

            override fun onDiscoveryStopped(serviceType: String) {
                // 当服务发现停止时调用
            }

            override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
                // 开始服务发现失败时调用
            }

            override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
                // 停止服务发现失败时调用
            }
        })
}
  • 在discoverServices方法中,第一个参数"_http._tcp."是服务类型。这是一种按照 DNS - SD(Domain Name System - Service Discovery)格式定义的服务类型。NsdManager.PROTOCOL_DNS_SD表示使用 DNS - SD 协议。DiscoveryListener是一个回调接口,用于接收服务发现过程中的各种事件。
  • 当发现一个服务(onServiceFound方法被调用)时,需要调用resolveService方法来解析服务,以获取更详细的信息,如服务的主机地址(host)和端口号(port)。

5.3 运行程序

将服务端Android设备和客户端的Android设备都连接到同一个局域网中。然后先启动服务端App,再启动客户端App,点击按钮进行查找,会走onServiceFound回调,并在resolveService中走onServiceResolved回调,这里可以获取到服务的主机地址(host)和端口号(port),至此,NSD扫描的整个流程就都走通了。

6. 本文源码

https://gitee.com/EthanCo/my-nsd-test

7. 参考

官方文档 : NsdManager
Android网络服务发现(NSD)使用


http://www.kler.cn/a/421269.html

相关文章:

  • 基于 MVC 架构的 SpringBoot 高校行政事务管理系统:设计优化与实现验证
  • 【计算机网络】实验6:IPV4地址的构造超网及IP数据报
  • 大R玩家流失预测在休闲社交游戏中的应用
  • TIE算法具体求解-为什么是泊松方程和傅里叶变换
  • Node.js 基础教程
  • 前端开发 之 15个页面加载特效上【附完整源码】
  • 第1章:CSS简介 --[CSS零基础入门]
  • Scala 的match case 匹配元组
  • zerotier实现内网穿透
  • M31系列LoRa分布式IO模块功能简介
  • 黑马2024AI+JavaWeb开发入门Day04-SpringBootWeb入门-HTTP协议-分层解耦-IOCDI飞书作业
  • 【微服务】SpringBoot 对接飞书多维表格事件回调监听流程详解
  • 基于Linux的逻辑订阅发布搭建
  • 多线程运行时,JVM(Java虚拟机)的内存模型
  • Runway 技术浅析(七):视频技术中的运动跟踪
  • [TLS] 使用 OpenSSL 命令行测试 TLS13 0-RTT
  • 关于机器学习领域的预测算法/模型基础入门
  • 【论文笔记】Frequency Domain Model Augmentation for Adversarial Attack
  • BioDeepAV:一个多模态基准数据集,包含超过1600个深度伪造视频,用于评估深度伪造检测器在面对未知生成器时的性能。
  • 【ETCD】ETCD用户密码认证
  • HTML5技术贴:现代网页开发的革命
  • 迁移学习!超高创新!GASF-AlexNet-MSA,基于格拉姆角场和AlexNet结合多头注意力机制的故障识别程序
  • 数据结构 - 排序(四):排序算法总结与对比
  • KVCKVO
  • uniapp:封装商品列表为组件并使用
  • 基于Redis海量数据场景分布式ID架构实践