android NSD服务详解

android NSD服务详解
一.NSD的基础知识:
NSD全称为: Network Service Discovery.翻译过来的意思就是发现服务器网络的意思。理解的说就是:发现服务器对应的网络信息。
作用为: The addition of Network Service Discovery (NSD) takes this further by allowing an application to seek out a nearby device running services with which it can communicate.
(也就是说使用这个服务就可以搜索附近哪个网络设备设备可以进行通信,一般指的是局域网内连接同一个wifi的信号源).

NSD的作用和意义
NSD只是发现网络,并非直接连接网络,发现网络就是发现某个局域网内可连网络,并且获取到对应设备的IP地址和端口号。

NSD的作用就是为下一步连接网络做准备,比如使用Socket网络连接同一局域网两个手机,并且进行数据通信,这就要知道服务器的ip地址和端口号。

Socket不限于蓝牙交互,通过IP和端口号也是可以进行网络交互的,并且效率也比较快,就是会麻烦一些。

二.关于NSD的开发的过程.
NSD(NsdManager)是Android SDK中自带的类库,可以直接调用编程。

这里我吐槽一下,网上很多示例代码都是比较坑爹的,基本步骤都是说什么注册、监听、处理、注销。。。
他们最大的失误是没有区分服务端和客户端。。。而是将代码混合在一起,刚开始接触的开发者就不知道怎么搞了!

其实NSD和Socket有点类似,都是有Server服务器端和Client客户端

对于Socket:
SocketServer服务器端需要定义端口号,然后进行监听连接
SocketClient客户端需要知道服务器端定义的端口号和服务器的ip地址,然后请求连接
然后在客户端进行发送请求连接,服务器端同意连接,这时Socket连接就建立起来的,就可以相互通信了。
对于NSD:
NSD Server服务器端定义主机名字、端口号,然后进行NSD注册
NSD Client客户端进行扫描,但是只能扫描到包含NSD Server服务器定义的主机名字的NsdServiceInfo对象
然后NSD Client客户端能根据这个NsdServiceInfo对象解析到服务器端的IP地址和它的端口号
到这里我们就应该知道了:NSD是为Socket连接做准备的其中一种手段!
1.NSD Server 服务器端的开发
服务器端很简单的,就一个注册就可以了
具体的步骤有:

(1)进行注册监听
private NsdManager.RegistrationListener mRegistrationListener;

//实例化注册监听器
    private void initializeRegistrationListener() {
        mRegistrationListener = new NsdManager.RegistrationListener() {
            @Override
            public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.e(TAG, "NsdServiceInfo onRegistrationFailed");

            }

            @Override
            public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.i(TAG, "onUnregistrationFailed serviceInfo: " + serviceInfo + " ,errorCode:" + errorCode);

            }

            @Override
            public void onServiceRegistered(NsdServiceInfo serviceInfo) {
                Log.i(TAG, "onServiceRegistered: " + serviceInfo);

            }

            @Override
            public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
                Log.i(TAG, "onServiceUnregistered serviceInfo: " + serviceInfo);

            }
        };
    }
1
2
3

注册监听是为了监听后面的注册服务器是否成功

(2)进行注册
  private NsdManager mNsdManager;
  //注册服务器端
   private void registerService(Context context, String serviceName, int port) {
        mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
        NsdServiceInfo serviceInfo = new NsdServiceInfo();
        serviceInfo.setServiceName(serviceName);
        serviceInfo.setPort(port);
        serviceInfo.setServiceType("_http._tcp.");//客户端发现服务器是需要对应的这个Type字符串
        mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
    }


11
12
到这里如果客户端马上进行扫描就能看到注册了NSD的服务器网络数据
接着就可以进行Socket连接,并且可以取消掉注册NSD服务器,就是让别人看不到你的NSD服务器

(3)取消注册
  public void stopNSDServer() {
        mNsdManager.unregisterService(mRegistrationListener);
    }
1
2
3
4
如果要一直暴露自己的ip和端口数据,这个NSDServer就不要关闭,开发中根据实际情况来。
反正我是一直没有关闭这个暴露的端口信息,即使Socket连接上了,也没有关闭(隐藏),让它一直暴露着也没什么大问题!

NSD服务器端就是这么简单,网上说的还要扫描,解析的那些都是在客户端来完成的。虽然也是NSD的一些操作方法,但是在服务器端是完全没有用到的。

2.NSD Client 客户端的开发
客户端分为两个步骤:扫描,解析
但是过程分为四步:

(1)注册扫描监听器
  //NSD管理器
  private NsdManager mNsdManager = null;
  private void initData() {
        mNsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE);
    }

  private NsdManager.DiscoveryListener mNSDDiscoveryListener = null;    //    搜寻监听器

   //注册NSD服务网络的监听,发现NSD网络后会在对应的方法回调
    private void initNSDDiscoveryListener() {
        mNSDDiscoveryListener = new NsdManager.DiscoveryListener() {
            @Override
            public void onStartDiscoveryFailed(String serviceType, int errorCode) {
                Log.i(TAG, "onStartDiscoveryFailed--> " + serviceType + ":" + errorCode);
            }

            @Override
            public void onStopDiscoveryFailed(String serviceType, int errorCode) {
                Log.i(TAG, "onStopDiscoveryFailed--> " + serviceType + ":" + errorCode);
            }

            @Override
            public void onDiscoveryStarted(String serviceType) {
                Log.i(TAG, "onDiscoveryStarted--> " + serviceType );
            }

            @Override
            public void onDiscoveryStopped(String serviceType) {
                Log.i(TAG, "onDiscoveryStopped--> " + serviceType );
            }

            @Override
            public void onServiceFound(NsdServiceInfo serviceInfo) {//关键的回调方法
        //这里的 serviceInfo里面只有NSD服务器的主机名,要解析后才能得到该主机名的其他数据信息
                 Log.i(TAG, onServiceFound Info: --> " + serviceInfo);
                //开始解析数据

            }

            @Override
            public void onServiceLost(NsdServiceInfo serviceInfo) {
        Log.i(TAG, "onServiceLost--> " + serviceInfo);
            }
        };
    }


47
(2)注册解析监听器
    private NsdManager.ResolveListener mNSDResolveListener = null;//    解析监听器

    //注册解析NSD网络的监听 ,解析NSD数据后回调
    private void initNSDResolveListener() {
        mNSDResolveListener = new NsdManager.ResolveListener() {
            @Override
            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {

            }

            @Override
            public void onServiceResolved(NsdServiceInfo serviceInfo) {//到这里就是我们要的最终数据信息
                Log.i(TAG, "resolution : " + serviceInfo.getServiceName() + " \n host_from_server: " + serviceInfo.getHost() +
                        "\n port from server: " + serviceInfo.getPort());
                String hostAddress = serviceInfo.getHost().getHostAddress();


                Log.i(TAG, "hostAddress ip--> " + hostAddress );


            }
        };

    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(3)扫描网络
   private final String mServerType = "_http._tcp.";

  //发现周边的NSD相关网络
    public void discoveryNSDServer(View view) {
        //三个参数
    //第一个参数要和NSD服务器端定的ServerType一样,
    //第二个参数是固定的
    //第三个参数是扫描监听器
        mNsdManager.discoverServices(mServerType, NsdManager.PROTOCOL_DNS_SD, mNSDDiscoveryListener);

    }
1
2
3
4
5
6
7
8
9
10
11
12
进行发现服务操作后,会在扫描监听器对应的方法得到数据。

(4)解析网络

   //对得到的NDSServiceInfo进行解析
   public void resoleServer(){

   //第一个参数是扫描得到的对象,第二个参数是解析监听对象
   mNsdManager.resolveService(nsdServiceInfo, mNSDResolveListener);
   }

1
2
3
4
5
6
7
8
9
对数据解析后就能在解析监听对象的回调方法中,获取到我们最终要的数据了。

有些人看网上那些例子,说客户端也要注册,才不会导致什么什么问题。但是我是没有注册的并且程序很正常。
按道理来说也是,不需要注册的,因为我们是要连接服务器端,自己并不需要作为服务器端,Socket连接也是这样的。
如果要自己是客户端,又是别人的服务器端,这种情况那就另说了。

对NSD的其他方面
NSD的Server服务器端的注册和监听最好是放在子线程中或者使用Android服务类Service来注册监听
同样Client客户端也是用子线程或Service来实现比较稳妥。
直接服务器端直接在Activity监听,回调,拿到里面的数据显示在Activity上面是有点问题的,我试过!
NSD还有一些其他的坑,比如客户端扫描NSD网络中,这个扫描监听器不能在执行一次扫描,要等它扫描完成,否则程序会崩溃。

关于NSD这里有个简单的示例,代码:
https://download.csdn.net/download/wenzhi20102321/10289492
程序基本功能还是可以的。就是现在不方便展示!
这个程序的主页是选择“Server页面”或者“Client”页面
点击选择“Server页面”进入服务器端页面,输入主机名、端口号,点击注册,稍等一下就显示是否注册成功
如果有另一个手机选择“Client页面”进入客户端页面,只有一个按钮选择dicoveryNSD,就会进行NSD网络发现,并且解析,最后显示扫描到的NSD网络的完整数据。

这里还有一个服务器端的封装代码,可以直接拿来用的
package com.nsd.lwx.nsddemo.nsd;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Log;

import com.nsd.lwx.nsddemo.utils.Constant;


/**
 * Description: NSD Server 的封装类
 */

public class NSDServer {
    public static final String TAG = "NSDServer";
    private NsdManager mNsdManager;
    private NsdManager.RegistrationListener mRegistrationListener;//注册监听器对象
    private String mServerName;
    private Context mContext;
    private int mPort;
    private String mServiceName;
    private final String mServerType = "_http._tcp.";

    //监听注册,并开始NSDServer注册
    public void startNSDServer(Context context, String serviceName, int port) {
        initializeRegistrationListener();
        registerService(context, serviceName, port);
    }

    //实例化注册监听器
    private void initializeRegistrationListener() {
        mRegistrationListener = new NsdManager.RegistrationListener() {
            @Override
            public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.e(TAG, "NsdServiceInfo onRegistrationFailed");
                if (registerState != null) {
                    registerState.onRegistrationFailed(serviceInfo, errorCode);
                }
            }

            @Override
            public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.i(TAG, "onUnregistrationFailed serviceInfo: " + serviceInfo + " ,errorCode:" + errorCode);
                if (registerState != null) {
                    registerState.onUnregistrationFailed(serviceInfo, errorCode);
                }
            }

            @Override
            public void onServiceRegistered(NsdServiceInfo serviceInfo) {
                mServerName = serviceInfo.getServiceName();
                Log.i(TAG, "onServiceRegistered: " + serviceInfo);
                Log.i(TAG, "mServerName onServiceRegistered: " + mServerName);
                if (registerState != null) {
                    registerState.onServiceRegistered(serviceInfo);
                }
            }

            @Override
            public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
                Log.i(TAG, "onServiceUnregistered serviceInfo: " + serviceInfo);
                if (registerState != null) {
                    registerState.onServiceUnregistered(serviceInfo);
                }
            }
        };
    }

    //注册NSD服务器端
    private void registerService(Context context, String serviceName, int port) {
        mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
        NsdServiceInfo serviceInfo = new NsdServiceInfo();
        serviceInfo.setServiceName(serviceName);
        serviceInfo.setPort(port);
        serviceInfo.setServiceType(mServerType);
        mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
    }

    //取消注册NSD服务器端
    public void stopNSDServer() {
        mNsdManager.unregisterService(mRegistrationListener);
    }


    //NSD服务注册监听接口
    public interface IRegisterState {
        void onServiceRegistered(NsdServiceInfo serviceInfo);     //注册NSD成功

        void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode);   //注册NSD失败

        void onServiceUnregistered(NsdServiceInfo serviceInfo);  //取消NSD注册成功

        void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode); //取消NSD注册失败

    }

    //NSD服务接口对象
    private IRegisterState registerState;


    //设置NSD服务接口对象
    public void setRegisterState(IRegisterState registerState) {
        this.registerState = registerState;
    }
}

1

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android应用程序中搜索MDNS服务,可以使用Android网络框架中的Network Service Discovery(NSD)API。以下是搜索MDNS服务的一般步骤: 1. 创建NsdManager实例: ``` NsdManager nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); ``` 2. 创建NsdManager.DiscoveryListener实例,并实现onDiscoveryStarted、onServiceFound、onServiceLost和onDiscoveryStopped等回调方法: ``` NsdManager.DiscoveryListener discoveryListener = new NsdManager.DiscoveryListener() { @Override public void onDiscoveryStarted(String regType) { // 发现服务 } @Override public void onServiceFound(NsdServiceInfo serviceInfo) { // 找到服务 } @Override public void onServiceLost(NsdServiceInfo serviceInfo) { // 丢失服务 } @Override public void onDiscoveryStopped(String serviceType) { // 停止搜索 } @Override public void onStartDiscoveryFailed(String serviceType, int errorCode) { // 开始搜索失败 } @Override public void onStopDiscoveryFailed(String serviceType, int errorCode) { // 停止搜索失败 } }; ``` 3. 创建NsdManager.ResolveListener实例,并实现onServiceResolved回调方法: ``` NsdManager.ResolveListener resolveListener = new NsdManager.ResolveListener() { @Override public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { // 解析服务信息失败 } @Override public void onServiceResolved(NsdServiceInfo serviceInfo) { // 解析服务信息成功 } }; ``` 4. 开始搜索MDNS服务: ``` nsdManager.discoverServices("_http._tcp", NsdManager.PROTOCOL_DNS_SD, discoveryListener); ``` 其中,第一个参数是服务的类型,可以根据实际情况进行修改。第二个参数是协议类型,NSD API支持DNS-SD协议和mDNS协议,这里使用DNS-SD协议。第三个参数是DiscoveryListener实例。 5. 在onServiceFound回调方法中,获取到NsdServiceInfo实例,然后调用它的resolve方法解析服务信息: ``` nsdManager.resolveService(serviceInfo, resolveListener); ``` 其中,第一个参数是NsdServiceInfo实例,第二个参数是ResolveListener实例。 6. 在onServiceResolved回调方法中,获取到服务的IP地址和端口号等信息,然后可以使用它们来连接服务。 注意,搜索MDNS服务需要在AndroidManifest.xml中添加以下权限: ``` <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" /> ``` 同时,如果应用程序运行在Android 9.0及以上版本的设备上,则需要在AndroidManifest.xml中添加以下配置: ``` <application ... android:usesCleartextTraffic="true" ... > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值