【Android应用开发技术:网络通信】网络服务可发现使用方法

作者:郭孝星
微博:郭孝星的新浪微博
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
Github:https://github.com/AllenWells

下面我们来演示一下如何使用Android的NSD服务,文章最后会给出工程源码。

一 注册NSD服务

1.1 创建NsdServiceInfo对象

NsdServiceInfo对象包含的信息可以帮助其他设备决定是否要连接到我们所提供的服务。

我们会为NsdServiceInfo设置以下参数:

  • ServiceName:服务名称,该名称对所有局域网络中的设备可见,另外,网络中的名称必须是独一无二的,如果发生重复,Android会自动处理,例如:同时出现了两个名为NsdChat的应用,其中一个会被自动转换为NsdChat(1)。
  • SetviceType:服务类型,即使用的通信协议和传输层协议,语法是“_protocol_transportlayer”,例如:网络服务是“_http._ctp”,打印服务是“_ipp._tcp”。
  • Port:端口号,在设置端口号时,应该尽量避免将其硬编码在代码中, 防止与其他应用产生冲突。解决方法是,不要硬编码,使用下一个可用的端口。不必担心其他应用无法知晓服务的端口号,因为该信息将包含在服务的广播包中。接收到广播后,其他应用将从广播包种得知服务端口号,并通过端口连接到你的服务上。
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);
    ....
}

1.2 注册RegistrationListener接口

RegistrationListener接口包含了注册到Android系统中的一系列回调函数,用来通知应用程序服务注册/注销的成功和失败。

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.
        }
    };
}

1.3 调用registerService()方法注册服务

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);
}

二 发现网络中的服务

2.1 配置发现监听器

NSD API通过使用该接口中的方法通知用户程序:
- 何时发现开始
- 何时发现失败
- 何时找到可用服务
- 何时服务丢失

在上述代码中,当发现了可用的服务时,程序做了以下检查:

  1. 比较发现的服务名称与本地的服务,判断是否发现的是本机的服务(这是合法的)。
  2. 检查服务的类型,确认是否可以接入。
  3. 检查服务的名称,确认接入了正确的应用。

我们并不需要每次都检查服务名称,仅当想要接入特定的应用时,再去判断。例如:应用只想与运行在其他设备上的相同应用通信。然而,如果应用仅仅想接入到一台网络打印机,那么看到服务类型是“_ipp._tcp”的服务就足够了。

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);
        }
    };
}

2.2 启动发现服务

当我们配置好发现回调函数后,就可以调用discoverService()函数, 该函数的参数包括试图发现的服务种类、发现使用的协议、以及上一步创建的监听者。

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

三 连接到网内服务

3.1 确认服务的连接信息

当发现了网上可接入的网络服务,调用resolveService()方法,并将实现了的NsdManager.RelolveListener的对象和在服务发现过程中得到的NsdServiceInfo对象传入,连接信息可以从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();
        }
    };
}

四 程序退出时注销服务

在应用的生命周期中正确的开启和关闭NSD服务是十分关键的。 在程序退出时注销服务可以防止其他程序因为不知道服务退出而反复尝试连接的行为。另外,服务发现是一种开销很大的操作,应该随着父Activity的暂停而停止,当用户返回该界面是再开启。 因此,我们应该重载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);
    }

附录

NsdChat工程源码下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值