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

0. 前言

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

1. NSD是什么

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

通过NSD扫描,Android应用可以获取到已经注册的服务的名称、端口号和IP地址,从而为后续的网络通信或连接做准备。

2. NSD扫描的应用场景

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

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

3. NSD使用的是DNS-SD协议

NSD使用的是DNS-SD (Domain Name System - Service Discovery)域名系统服务发现协议,用于在网络中自动发现可用的服务和设备。

而DNS-SD 协议则利用了DNS协议的一些特性和机制。

目的:DNS协议的主要目的是将域名解析为IP地址,以方便人们访问互联网。而DNS-SD协议的主要目的是在局域网内自动发现和连接网络服务。

应用场景 : DNS协议通常用于全局范围内的域名解析,而DNS-SD协议则主要应用于局域网内的服务发现。

实现机制:虽然DNS-SD协议利用了DNS协议的一些特性和机制(如DNS报文格式、记录类型等),但它还引入了特定的DNS记录类型(如PTR、SRV、TXT等)来进行服务的发现和信息传递。此外,DNS-SD协议还依赖于mDNS协议来实现设备自我注册和查询等功能。

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

NSD允许应用在同一局域网内自动发现其他设备提供的服务,并与之建立连接。

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

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

猜你喜欢

转载自blog.csdn.net/EthanCo/article/details/144149822
今日推荐