【Android应用开发技术:网络通信】网络服务可发现使用方法
2015-08-06 13:23
1056 查看
作者:郭孝星
微博:郭孝星的新浪微博
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
Github:https://github.com/AllenWells
下面我们来演示一下如何使用Android的NSD服务,文章最后会给出工程源码。
我们会为NsdServiceInfo设置以下参数:
ServiceName:服务名称,该名称对所有局域网络中的设备可见,另外,网络中的名称必须是独一无二的,如果发生重复,Android会自动处理,例如:同时出现了两个名为NsdChat的应用,其中一个会被自动转换为NsdChat(1)。
SetviceType:服务类型,即使用的通信协议和传输层协议,语法是“_protocol_transportlayer”,例如:网络服务是“_http._ctp”,打印服务是“_ipp._tcp”。
Port:端口号,在设置端口号时,应该尽量避免将其硬编码在代码中, 防止与其他应用产生冲突。解决方法是,不要硬编码,使用下一个可用的端口。不必担心其他应用无法知晓服务的端口号,因为该信息将包含在服务的广播包中。接收到广播后,其他应用将从广播包种得知服务端口号,并通过端口连接到你的服务上。
- 何时发现开始
- 何时发现失败
- 何时找到可用服务
- 何时服务丢失
在上述代码中,当发现了可用的服务时,程序做了以下检查:
比较发现的服务名称与本地的服务,判断是否发现的是本机的服务(这是合法的)。
检查服务的类型,确认是否可以接入。
检查服务的名称,确认接入了正确的应用。
我们并不需要每次都检查服务名称,仅当想要接入特定的应用时,再去判断。例如:应用只想与运行在其他设备上的相同应用通信。然而,如果应用仅仅想接入到一台网络打印机,那么看到服务类型是“_ipp._tcp”的服务就足够了。
微博:郭孝星的新浪微博
邮箱: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通过使用该接口中的方法通知用户程序:- 何时发现开始
- 何时发现失败
- 何时找到可用服务
- 何时服务丢失
在上述代码中,当发现了可用的服务时,程序做了以下检查:
比较发现的服务名称与本地的服务,判断是否发现的是本机的服务(这是合法的)。
检查服务的类型,确认是否可以接入。
检查服务的名称,确认接入了正确的应用。
我们并不需要每次都检查服务名称,仅当想要接入特定的应用时,再去判断。例如:应用只想与运行在其他设备上的相同应用通信。然而,如果应用仅仅想接入到一台网络打印机,那么看到服务类型是“_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工程源码下载相关文章推荐
- 【Android应用开发技术:网络通信】网络服务可发现基本原理
- 人证比对TCPSERVER
- linux网络编程 no route to host 解决方案
- iOS iOS9 beta 请求出现App Transport Security has blocked a cleartext HTTP (http://)
- android请求网络——第三方库android-async-http的使用(2)
- 为什么DMOZ对于网络如此重要?
- php发送http请求的几种方法
- 2015 Multi-University Training Contest 5(hdu5343 - 5352)网络流+yy+(哈夫曼树+dp)
- HTTP状态码整理
- 不用sdwebimage获取网络图片
- 检测网络是否可用
- Ubuntu下VirtualBox虚拟机与主机机互相ping通的简单配置心得
- TCP协议疑难杂症全景解析
- iOS系统网络抓包方法
- 计算机网络
- 解决FaustCplus的两个bug
- HTTP状态 301、404、200、304、500 等等
- TCP协议的那些事(总结篇)
- Android训练课程(Android Training) - 使用Volley传输网络数据(Transmitting Network Data Using Volley)
- HTTP 1.1 与HTTP 1.0 的比较