Training->Using Network Service Discovery

在你的应用中增加Network Service Discovery(NSD), 可以让你的用户在同一个网络中找到需求的服务。这对各种点对点的应用非常有用,例如文件共享或者多人一起游戏。Android  NSD的API可以让你轻易的实现这些功能

这一课教大家如何去广播自己设备服务的名字和网络信息,如何去获取其他设备服务的名字和网络信息。最后讲的是,如何使用不同的设备连接到同一个使用中。

Register Your Service on the Network

 注:这一步是可选的。如果你不需要广播你的服务信息,可以跳过这一步,直接到下一个章节 Discover Services on the Network.

要在局域网络中,注册一个服务,首先要创建一个NsdServiceInfo 对象,这个对象为其他设备提供连接信息,如服务名、IP和端口号。

public void registerService(int port) {
    // Create the NsdServiceInfo object, and populate it.
    NsdServiceInfo serviceInfo  = new NsdServiceInfo();

    // The name is subject to change based on conflicts
    // with other services advertised on the same network.
    serviceInfo.setServiceName("NsdChat");
    serviceInfo.setServiceType("_http._tcp.");
    serviceInfo.setPort(port);
    ....
}
这个代码片段把服务的名字设成NsdChat。这个名字对局域网中其他使用NSD查找设备的服务是可见的。在局域网中,这个名字是唯一的,Android会自动处理名字冲突。如果两个名字冲突,例如两个名字都位NsdChart,其中一个就会被改成NsdChart(1)
第二个参数设置服务的类型,指定服务使用哪种协议和传输层。语法是"_<protocol>._<transportlayer>。在上面的代码片段中,服务使用的是TCP HTTP协议。如果一个提供打印服务的应用,它的类型将会设成 "_ipp._tcp".

注:国际号码分配机构(IANA)负责管理NSD和Bonjour发现协议的所有服务类型。可以到 theIANA list of service names and port numbers下载服务类型的列表。如果你需要使用一个新的服务类型,你应该通过填写IANA端口和服务登记表保留它。

当为你的服务设置端口号时,不要使用硬编码,才不会和其他应用冲突。例如,假设你的应用一直使用1337端口,万一别的应用也使用了这个端口,那么这两个应用就会冲突。所以,要使用设备的自动分配的空闲端口。因为端口的信息是通过广播告知其他设备服务的,所以端口号不需要在代码编译的时候就确定好。其他应用可以通过你发送的服务广播,获取端口信息,然后再连接你的服务。

如果你使用socket,把参数设为0系统就会自动分配一个可用的端口号。

public void initializeServerSocket() {
    // Initialize a server socket on the next available port.
    mServerSocket = new ServerSocket(0);

    // Store the chosen port.
    mLocalPort =  mServerSocket.getLocalPort();
    ...
}


至此,你已经定义了  NsdServiceInfo对象,你需要实现 RegistrationListener。这个接口在服务注册成功或失败的时候,会回调给你。
public void initializeRegistrationListener() {
    mRegistrationListener = new NsdManager.RegistrationListener() {

        @Override
        public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
            // Save the service name.  Android may have changed it in order to
            // resolve a conflict, so update the name you initially requested
            // with the name Android actually used.
            mServiceName = NsdServiceInfo.getServiceName();
        }

        @Override
        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Registration failed!  Put debugging code here to determine why.
        }

        @Override
        public void onServiceUnregistered(NsdServiceInfo arg0) {
            // Service has been unregistered.  This only happens when you call
            // NsdManager.unregisterService() and pass in this listener.
        }

        @Override
        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Unregistration failed.  Put debugging code here to determine why.
        }
    };
}
现在你已经拥有了注册服务的所有信息了,调用 registerService()方法进行注册。
注意,这个方法是异步的,所以,所有需要在服务注册成功后的方法调用,需要在 onServiceRegistered()中调用。

public void registerService(int port) {
    NsdServiceInfo serviceInfo  = new NsdServiceInfo();
    serviceInfo.setServiceName("NsdChat");
    serviceInfo.setServiceType("_http._tcp.");
    serviceInfo.setPort(port);

    mNsdManager = Context.getSystemService(Context.NSD_SERVICE);

    mNsdManager.registerService(
            serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}

Discover Services on the Network

在生活中的很多方面,都有网络的身影,如网络打印机、网络摄像头等功能生态系统。让你的应用发现这个充满活力的生态系统的关键是发现服务。你的应用需要监听网络中服务的广播,才能知道哪些服务可用,并过滤掉你不需要的服务。

服务发现和服务注册一样,有两步:实现一个监听回调接口;调用异步API discoverServices().

实现使用一个匿名类实现 NsdManager.DiscoveryListener。下面的代码片段是一个简单的例子。

public void initializeDiscoveryListener() {

    // Instantiate a new DiscoveryListener
    mDiscoveryListener = new NsdManager.DiscoveryListener() {

        //  Called as soon as service discovery begins.
        @Override
        public void onDiscoveryStarted(String regType) {
            Log.d(TAG, "Service discovery started");
        }

        @Override
        public void onServiceFound(NsdServiceInfo service) {
            // A service was found!  Do something with it.
            Log.d(TAG, "Service discovery success" + service);
            if (!service.getServiceType().equals(SERVICE_TYPE)) {
                // Service type is the string containing the protocol and
                // transport layer for this service.
                Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
            } else if (service.getServiceName().equals(mServiceName)) {
                // The name of the service tells the user what they'd be
                // connecting to. It could be "Bob's Chat App".
                Log.d(TAG, "Same machine: " + mServiceName);
            } else if (service.getServiceName().contains("NsdChat")){
                mNsdManager.resolveService(service, mResolveListener);
            }
        }

        @Override
        public void onServiceLost(NsdServiceInfo service) {
            // When the network service is no longer available.
            // Internal bookkeeping code goes here.
            Log.e(TAG, "service lost" + service);
        }

        @Override
        public void onDiscoveryStopped(String serviceType) {
            Log.i(TAG, "Discovery stopped: " + serviceType);
        }

        @Override
        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            mNsdManager.stopServiceDiscovery(this);
        }

        @Override
        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            mNsdManager.stopServiceDiscovery(this);
        }
    };
}
NSD的API在这个接口的方法中,告诉你何时开始查找设备,何时开始查找失败,何时找到服务,何时失去服务等。注意到,在找到服务的时候,上面的代码片段做了很多比较。

1、找到的服务名称和自己的服务名称作比较,看看收到的是否是自己的服务。

2、检查服务类型,看看是否是自己需要的类型

3、检查服务名字,看看是否是需要的服务名。

检查服务的名字不是必要的,只有你要连接到一个特别的应用的时候,才需要检查。例如,服务想要连接到跑在其他设备的同一个应用。

如果应用想要连接到一个打印机,检查类型"_ipp._tcp"就够了。

实现回调接口后,调用discoverServices(),传给它你需要的服务类型,使用的发现协议和你实现的回调对象。

mNsdManager.discoverServices(
        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

Connect to Services on the Network

当你的应用找到一个可以连接的服务,首先要解析出服务的连接信息,使用 resolveService()方法。实现一个NsdManager.ResolveListener作为resolveService()的参数,用它来获取包含连接信息的NsdServiceInfo

public void initializeResolveListener() {
    mResolveListener = new NsdManager.ResolveListener() {

        @Override
        public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Called when the resolve fails.  Use the error code to debug.
            Log.e(TAG, "Resolve failed" + errorCode);
        }

        @Override
        public void onServiceResolved(NsdServiceInfo serviceInfo) {
            Log.e(TAG, "Resolve Succeeded. " + serviceInfo);

            if (serviceInfo.getServiceName().equals(mServiceName)) {
                Log.d(TAG, "Same IP.");
                return;
            }
            mService = serviceInfo;
            int port = mService.getPort();
            InetAddress host = mService.getHost();
        }
    };
}

当解析完成后,就可以获取到连接的详细信息,如IP和端口号。这是连接到其他服务需要的所有信息。

Unregister Your Service on Application Close

在应用的生命周期中,要在适当的时间打开和关系NSD功能。在应用关闭的时候取消注册,可以防止其他应用连接到关闭的服务。另外,发现服务是一个开销很大的操作,所以在Activity paused的时候,也要停止搜索;当Activityresumed的时候,再重新搜索。重载主Activity的生命周期的方法,在合适的地方插入开始和停止服务的广播、开始和停止搜索服务。

//In your application's Activity

    @Override
    protected void onPause() {
        if (mNsdHelper != null) {
            mNsdHelper.tearDown();
        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mNsdHelper != null) {
            mNsdHelper.registerService(mConnection.getLocalPort());
            mNsdHelper.discoverServices();
        }
    }

    @Override
    protected void onDestroy() {
        mNsdHelper.tearDown();
        mConnection.tearDown();
        super.onDestroy();
    }

    // NsdHelper's tearDown method
        public void tearDown() {
        mNsdManager.unregisterService(mRegistrationListener);
        mNsdManager.stopServiceDiscovery(mDiscoveryListener);
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值