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

news2024/12/26 22:20:26

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.coloradmin.cn/o/2254180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ROS2 系列学习教程(总目录)

ROS2Learning ROS1 系列学习教程(总目录) 一、ROS2 简介 1.1 ROS2简介及学习资源汇总 二、ROS2 基础 2.1 ROS2安装详细教程&#xff08;以Humble为例&#xff09; 2.2 ROS2 构建系统 colcon 介绍、安装与使用 2.3 ROS2 与 ROS1 编码方式对比 ROS2 与 ROS1 编码方式对比&am…

万字长文解读深度学习——VQ-VAE和VQ-VAE-2

&#x1f33a;历史文章列表&#x1f33a; 深度学习——优化算法、激活函数、归一化、正则化 深度学习——权重初始化、评估指标、梯度消失和梯度爆炸 深度学习——前向传播与反向传播、神经网络&#xff08;前馈神经网络与反馈神经网络&#xff09;、常见算法概要汇总 万字长…

Vue 组件通信全面解析

Vue 组件通信全面解析&#xff1a;方式、原理、优缺点及最佳实践 在 Vue 开发中&#xff0c;组件通信是一个重要的核心问题。随着应用复杂度的增加&#xff0c;如何在组件之间有效传递数据、触发事件&#xff0c;直接影响代码的可维护性和可扩展性。Vue 提供了多种组件通信方式…

对力扣77组合优化的剪枝操作的理解

77. 组合 代码随想录放出了这一张图 我乍一看觉得想当然,但是仔细想想,又不知道以下剪枝代码作何解释,因此我想通过这篇文章简要解释一下 class Solution { private:vector<vector<int>> result;vector<int> path;void backtracking(int n, int k, int sta…

SpringMVC其他扩展

一、全局异常处理机制: 1.异常处理两种方式: 开发过程中是不可避免地会出现各种异常情况的&#xff0c;例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题&#xff0c;甚至直接导致程序崩溃。因此&#xff0c;在开发过程中&#xff0c;…

运行 GreatSQL 时为什么要求关闭透明大页

在大部分运维规范中&#xff0c;一般都会要求在运行 GreatSQL/MySQL 的环境中要关闭透明大页&#xff0c;那么到底什么是透明大页&#xff0c;为什么要关闭&#xff0c;打开有什么风险吗&#xff1f; 在此之前&#xff0c;我也是有点懵的&#xff0c;本文试着回答这个疑问&…

日本IT|AWS技术方向都需要做哪些工作呢?

在日本IT行业中&#xff0c;AWS&#xff08;亚马逊网络服务&#xff09;技术方向的工作主要涉及利用AWS提供的各种服务和工具来构建、部署和管理云计算解决方案。具体来说&#xff0c;AWS技术方向的工作内容可能包括但不限于以下几个方面&#xff1a; 架构设计&#xff1a; 根据…

PostgreSQL实现透视表查询

PostgreSQL 8.3版本发布时&#xff0c;引入了一个名为tablefunc的新扩展。这个扩展提供了一组非常有趣的函数。其中之一是交叉表函数&#xff0c;用于创建数据透视表。这就是我们将在本文中讨论的内容。 需求说明 解释此函数如何工作的最简单方法是使用带有数据透视表的示例…

生信软件开发1 - 设计一个简单的Windwos风格的GUI报告软件

1. 安装基础库 使用Windows 11标题样式和主题自定义UI窗口库pywinstyles&#xff08;github: https://github.com/Akascape/py-window-styles&#xff09;&#xff0c;结合python自带tkinter库设计一个报告GUI软件。 pip install pywinstyles2. 设计一个简单的Windwos风格的G…

navicat连接mysql 8.0以上版本2059错误

安装了最新版本8.0.4的mysql之后&#xff0c;使用navicat链接提示以下错误。原因是因为mysql8.0 之前的版本中加密规则是 mysql_native_password&#xff0c;而 mysql8.0 之后的版本加密规则是caching_sha2_password 处理方案 解决方案1&#xff1a;下载安装最新版本navicat…

使用docker快速部署Nginx、Redis、MySQL、Tomcat以及制作镜像

文章目录 应用快速部署NginxRedisMySQLTomcat 制作镜像镜像原理基于已有容器创建使用 Dockerfile 创建镜像指令说明构建应用创建 Dockerfile 文件创建镜像 应用快速部署 Nginx docker run -d -p 80:80 nginx使用浏览器访问虚拟机地址 Redis docker pull redis docker run --…

【PlantUML系列】类图(一)

目录 一、类 二、接口 三、抽象类 四、泛型类 五、类之间的关系 六、添加注释 七、包图 八、皮肤参数 一、类 使用class关键字定义类&#xff0c;类名后跟大括号&#xff0c;声明类的属性和方法。 属性&#xff1a;格式为{visibility} attributeName : AttributeType…

VR眼镜可视化编程:开启医疗信息系统新纪元

一、引言 随着科技的飞速发展&#xff0c;VR 可视化编程在医疗信息系统中的应用正逐渐成为医疗领域的新趋势。它不仅为医疗教育、手术培训、疼痛管理等方面带来了新的机遇&#xff0c;还在提升患者体验、推动医疗信息系统智能化等方面发挥着重要作用。 在当今医疗领域&#xf…

IS-IS的原理

IS-IS的基本概念&#xff1a; 概述&#xff1a; IS-IS&#xff0c;中间系统到中间系统&#xff0c;是ISO国际标准化组织为它的无连接网络协议设计的一种动态路由协议 IS-IS支持CLNP网络和IP网络&#xff0c;采用数据链路层封装&#xff0c;区别于ospf只支持IP网络&#xff0…

华为ACL应用笔记

1、基本ACL 2000-2999 基本ACL&#xff08;Access Control List&#xff0c;访问控制列表&#xff09;是一种网络安全技术&#xff0c;它根据源IP地址、分片信息和生效时间段等信息来定义规则&#xff0c;对报文进行过滤。 规则&#xff1a; ACL由一系列规则组成&#xff0c;每…

点云标注软件SUSTechPOINTS的安装和使用,自测win10和ubuntu20.04下都可以用

点云标注软件SUSTechPOINTS的安装和使用 github项目源码&#xff1a;https://github.com/naurril/SUSTechPOINTS gitee源码以及使用教程&#xff1a;https://gitee.com/cuge1995/SUSTechPOINTS 首先拉取源码 git clone https://github.com/naurril/SUSTechPOINTS最好是在cond…

大模型评测中的基础指标都包括哪些

大语言模型&#xff08;LLM&#xff09;评测是LLM开发和应用中的关键环节。目前评测方法可以分为人工评测和自动评测&#xff0c;其中&#xff0c;自动评测技术相比人工评测来讲&#xff0c;具有效率高、一致性好、可复现、鲁棒性好等特点&#xff0c;逐渐成为业界研究的重点。…

SystemUI修改状态栏电池图标样式为横屏显示(以Android V为例)

SystemUI修改状态栏电池图标样式为横屏显示(以Android V为例) 1、概述 在15.0的系统rom产品定制化开发中&#xff0c;对于原生系统中SystemUId 状态栏的电池图标是竖着显示的&#xff0c;一般手机的电池图标都是横屏显示的 可以觉得样式挺不错的&#xff0c;所以由于产品开发…

最长最短单词

最长最短单词 C语言实现C实现Java实现Python实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输入1行句子&#xff08;不多于200个单词&#xff0c;每个单词长度不超过100&#xff09;&#xff0c;只包含字母、空格和逗号。单词由至少一…

C++【PCL】点云数据平移 旋转,对点云进行刚体变化

//头文件 #include <iostream>#include <pcl/point_cloud.h>#include<pcl/io/pcd_io.h>#include <pcl/common/transforms.h> #include <pcl/io/ply_io.h>//主函数int main() {pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud(new pcl::…