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)使用

相关推荐
高林雨露4 小时前
ImageView android:scaleType各种属性
android·imageview各种属性
Hi-Dison5 小时前
OpenHarmony系统中实现Android虚拟化、模拟器相关的功能,包括桌面显示,详细解决方案
android
事业运财运爆棚6 小时前
http 502 和 504 的区别
android
峥嵘life8 小时前
Android Studio新版本的一个资源id无法找到的bug解决
android·bug·android studio
编程乐学8 小时前
网络资源模板--Android Studio 实现绿豆通讯录
android·前端·毕业设计·android studio·大作业·安卓课设·绿豆通讯录
朴拙数科11 小时前
mysql报错解决 `1525 - Incorrect DATETIME value: ‘0000-00-00 00:00:00‘`
android·数据库·mysql
1登峰造极13 小时前
scroll-view 实现滑动显示,确保超出正常显示,小程序app,h5兼容
android·小程序
刘争Stanley13 小时前
Android 15(V)新功能适配,雕琢移动细节之美
android·kotlin·android 15·android v
小狗爱世界13 小时前
深入解析Binder源码
android·binder
qq_2146703513 小时前
android 聊天界面键盘、表情切换丝滑
android·gitee