Android开发中的NSD扫描是什么

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 添加权限
xml 复制代码
<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 管理类
kotlin 复制代码
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用于接收注册过程中的各种事件。

kotlin 复制代码
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 动态获取端口号

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

kotlin 复制代码
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方法来停止服务发现。

kotlin 复制代码
override fun onDestroy() {
    super.onDestroy()

    mNsdManager.unregisterService(registrationListener)
}

5.2 客户端的实现

5.2.1 添加权限
xml 复制代码
<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 管理类
kotlin 复制代码
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
//...

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

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

kotlin 复制代码
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)使用

相关推荐
我命由我1234532 分钟前
Android Studio 警告信息:Use start instead of left to ensure...
android·java·开发语言·ide·java-ee·android studio·android-studio
沈剑心36 分钟前
gson很好,但我劝你在Kotlin上使用kotlinx.serialization
android·java·kotlin
FatherOfCodingMan1 小时前
unity adb 连不上安卓手机?
android·adb·智能手机
daban20081 小时前
【技术支持】安卓无线adb调试连接方式
android·adb
SCBAiotAigc2 小时前
Android Studio历史版本包加载不出来,怎么办?
android·ide·android studio
用户10755617524072 小时前
Android让我的迷惑的windowSoftInputMode(二)
android
十二测试录3 小时前
Android SDK下载安装(图文详解)
android·经验分享·python·程序人生·adb·自动化
誓约酱4 小时前
Linux下ext2文件系统
android·linux·c语言·数据库·c++·后端·ubuntu
KeyPan6 小时前
【Ubuntu与Linux操作系统:十二、LAMP平台与PHP开发环境】
android·java·linux·运维·服务器·ubuntu
目目沐沐6 小时前
过滤器模式
android·java·windows