在我们的app中添加网络服务发现功能(NSD)以方便在不同的设备上响应局域网中的请求。这种功能对于多设备之间点对点服务来说很有用,例如多人游戏,多人通话,文件共享等。

一,在网络中注册你的服务

注意:这一步是可选操作,如果你对通过局域网广播你的应用服务不关心,这一步大可省去。

在局域网中注册你的服务,首先需要创建一个NsdServiceInfo对象。这个对象封装了局域网内其他设备连接你的服务的信息。

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

// 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会自动处理这种冲突。例如如果在同一个局域网中的两个设备都安装了具备NsdChat服务的应用程序,其中一个应用程序会自动修改服务的名称,如”NsdChat(2)“。

此外这个代码片段也设置了服务的类型,以定义应用程序所使用的传输层和传输协议。具体语法规则中为”_<protocol>._<transportlayer>".在代码片段中使用HTTP协议和TCP传输控制协议。如果一个应用程序提供了打印服务就需要设置服务类型为"_ipp._tcp"。

为了便于集中管理,互联网名称和数字地址分配机构制定了一些权威的服务发现协议标准,其中包括NSD和Bonjour。

在为你的服务设置端口的时候,应该避免与其他应用程序产生冲突。

如果你正在使用套接字,下面的代码片段可以告诉你如何简单地初始化一个套接字 :

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

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

 现在你可以定义一个NsdServiceInfo对象了。此时你需要实现RegistrationListener接口。这个接口包涵了一些回调函数,这些回调函数用来警告你的应用程序是否能够成功注册你的服务,或者告诉你是否能够成功取消注册服务。

public void initializeRegistrationListener(){
 mRegistrationListener =  new NsdManager.RegistrationListener(){

@Override
public void onServiceRegistered(NsdServiceInfoNsdServiceInfo){
// 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.
}
};
}


 

二,在网络中发现你的服务

在网络中发现我们的服务,就需要我们的应用程序时刻监听网络中可用的服务广播并且进行过滤。服务发现,就像注册服务一样分两步,第一设置一个搜索服务的监听器和与之相关的回调函数,并且创建一个异步的方法discoverServices()。

public void initializeDiscoveryListener(){

// Instantiate a new DiscoveryListener
 mDiscoveryListener = newNsdManager.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());
} elseif(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);
} elseif(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.检查服务名称是否可以被正确的应用程序成功的验证连接。

设置好监听器后,调用discoverServices()方法

mNsdManager.discoverServices(SERVICE_TYPE,NsdManager.PROTOCOL_NSD_SD,mDsicoveryListener);

三,在服务中连接你的服务

当你的应用程序在网络中发现了一个可连接的服务的时候,首先使用resolveService()方法确定可连接服务的信息,实现NsdManager.ResolverListener接口,并且从NsdServiceInfo中得到其中所封装的消息。

public void initializeResolveListener(){
     mResolveListener = newNsdManager.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();
         }
     };
}

四,应用程序关闭时应取消注册的服务

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