1page.title=Using Network Service Discovery 2 3trainingnavtop=true 4 5@jd:body 6 7<div id="tb-wrapper"> 8<div id="tb"> 9 10<!-- table of contents --> 11<h2>This lesson teaches you how to</h2> 12<ol> 13 <li><a href="#register">Register Your Service on the Network</a></li> 14 <li><a href="#discover">Discover Services on the Network</a></li> 15 <li><a href="#connect">Connect to Services on the Network</a></li> 16 <li><a href="#teardown">Unregister Your Service on Application Close</a></li> 17</ol> 18 19<!-- 20<h2>You should also read</h2> 21 <ul> 22 </ul> 23--> 24<h2>Try it out</h2> 25 26<div class="download-box"> 27 <a href="{@docRoot}shareables/training/NsdChat.zip" class="button">Download 28 the sample app</a> 29 <p class="filename">NsdChat.zip</p> 30</div> 31</p> 32 33</div> 34</div> 35 36<p>Adding Network Service Discovery (NSD) to your app allows your users to 37identify other devices on the local network that support the services your app 38requests. This is useful for a variety of peer-to-peer applications such as file 39sharing or multi-player gaming. Android's NSD APIs simplify the effort required 40for you to implement such features.</p> 41 42<p>This lesson shows you how to build an application that can broadcast its 43name and connection information to the local network and scan for information 44from other applications doing the same. Finally, this lesson shows you how 45to connect to the same application running on another device.</p> 46 47<h2 id="register">Register Your Service on the Network</h2> 48 49<p class="note"><strong>Note: </strong>This step is optional. If 50you don't care about broadcasting your app's services over the local network, 51you can skip forward to the 52next section, <a href="#discover">Discover Services on the Network</a>.</p> 53 54<p>To register your service on the local network, first create a {@link 55android.net.nsd.NsdServiceInfo} object. This object provides the information 56that other devices on the network use when they're deciding whether to connect to your 57service. </p> 58 59<pre> 60public void registerService(int port) { 61 // Create the NsdServiceInfo object, and populate it. 62 NsdServiceInfo serviceInfo = new NsdServiceInfo(); 63 64 // The name is subject to change based on conflicts 65 // with other services advertised on the same network. 66 serviceInfo.setServiceName("NsdChat"); 67 serviceInfo.setServiceType("_http._tcp."); 68 serviceInfo.setPort(port); 69 .... 70} 71</pre> 72 73<p>This code snippet sets the service name to "NsdChat". 74The name is visible to any device on the network that is using NSD to look for 75local services. Keep in mind that the name must be unique for any service on the 76network, and Android automatically handles conflict resolution. If 77two devices on the network both have the NsdChat application installed, one of 78them changes the service name automatically, to something like "NsdChat 79(1)".</p> 80 81<p>The second parameter sets the service type, specifies which protocol and transport 82layer the application uses. The syntax is 83"_<protocol>._<transportlayer>". In the 84code snippet, the service uses HTTP protocol running over TCP. An application 85offering a printer service (for instance, a network printer) would set the 86service type to "_ipp._tcp".</p> 87 88<p class="note"><strong>Note: </strong> The International Assigned Numbers 89Authority (IANA) manages a centralized, 90authoritative list of service types used by service discovery protocols such as NSD and Bonjour. 91You can download the list from <a 92 href="http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml">the 93IANA list of service names and port numbers</a>. 94If you intend to use a new service type, you should reserve it by filling out 95the <a 96 href="http://www.iana.org/form/ports-services">IANA Ports and Service 97 registration form</a>.</p> 98 99<p>When setting the port for your service, avoid hardcoding it as this 100conflicts with other applications. For instance, assuming 101that your application always uses port 1337 puts it in potential conflict with 102other installed applications that use the same port. Instead, use the device's 103next available port. Because this information is provided to other apps by a 104service broadcast, there's no need for the port your application uses to be 105known by other applications at compile-time. Instead, the applications can get 106this information from your service broadcast, right before connecting to your 107service.</p> 108 109<p>If you're working with sockets, here's how you can initialize a socket to any 110available port simply by setting it to 0.</p> 111 112<pre> 113public void initializeServerSocket() { 114 // Initialize a server socket on the next available port. 115 mServerSocket = new ServerSocket(0); 116 117 // Store the chosen port. 118 mLocalPort = mServerSocket.getLocalPort(); 119 ... 120} 121</pre> 122 123<p>Now that you've defined the {@link android.net.nsd.NsdServiceInfo 124NsdServiceInfo} object, you need to implement the {@link 125android.net.nsd.NsdManager.RegistrationListener RegistrationListener} interface. This 126interface contains callbacks used by Android to alert your application of the 127success or failure of service registration and unregistration. 128</p> 129<pre> 130public void initializeRegistrationListener() { 131 mRegistrationListener = new NsdManager.RegistrationListener() { 132 133 @Override 134 public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) { 135 // Save the service name. Android may have changed it in order to 136 // resolve a conflict, so update the name you initially requested 137 // with the name Android actually used. 138 mServiceName = NsdServiceInfo.getServiceName(); 139 } 140 141 @Override 142 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 143 // Registration failed! Put debugging code here to determine why. 144 } 145 146 @Override 147 public void onServiceUnregistered(NsdServiceInfo arg0) { 148 // Service has been unregistered. This only happens when you call 149 // NsdManager.unregisterService() and pass in this listener. 150 } 151 152 @Override 153 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 154 // Unregistration failed. Put debugging code here to determine why. 155 } 156 }; 157} 158</pre> 159 160<p>Now you have all the pieces to register your service. Call the method 161{@link android.net.nsd.NsdManager#registerService registerService()}. 162</p> 163 164<p>Note that this method is asynchronous, so any code that needs to run 165after the service has been registered must go in the {@link 166android.net.nsd.NsdManager.RegistrationListener#onServiceRegistered(NsdServiceInfo) 167onServiceRegistered()} method.</p> 168 169<pre> 170public void registerService(int port) { 171 NsdServiceInfo serviceInfo = new NsdServiceInfo(); 172 serviceInfo.setServiceName("NsdChat"); 173 serviceInfo.setServiceType("_http._tcp."); 174 serviceInfo.setPort(port); 175 176 mNsdManager = Context.getSystemService(Context.NSD_SERVICE); 177 178 mNsdManager.registerService( 179 serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); 180} 181</pre> 182 183<h2 id="discover">Discover Services on the Network</h2> 184<p>The network is teeming with life, from the beastly network printers to the 185docile network webcams, to the brutal, fiery battles of nearby tic-tac-toe 186players. The key to letting your application see this vibrant ecosystem of 187functionality is service discovery. Your application needs to listen to service 188broadcasts on the network to see what services are available, and filter out 189anything the application can't work with.</p> 190 191<p>Service discovery, like service registration, has two steps: 192 setting up a discovery listener with the relevant callbacks, and making a single asynchronous 193API call to {@link android.net.nsd.NsdManager#discoverServices(String 194, int , NsdManager.DiscoveryListener) discoverServices()}.</p> 195 196<p>First, instantiate an anonymous class that implements {@link 197android.net.nsd.NsdManager.DiscoveryListener}. The following snippet shows a 198simple example:</p> 199 200<pre> 201public void initializeDiscoveryListener() { 202 203 // Instantiate a new DiscoveryListener 204 mDiscoveryListener = new NsdManager.DiscoveryListener() { 205 206 // Called as soon as service discovery begins. 207 @Override 208 public void onDiscoveryStarted(String regType) { 209 Log.d(TAG, "Service discovery started"); 210 } 211 212 @Override 213 public void onServiceFound(NsdServiceInfo service) { 214 // A service was found! Do something with it. 215 Log.d(TAG, "Service discovery success" + service); 216 if (!service.getServiceType().equals(SERVICE_TYPE)) { 217 // Service type is the string containing the protocol and 218 // transport layer for this service. 219 Log.d(TAG, "Unknown Service Type: " + service.getServiceType()); 220 } else if (service.getServiceName().equals(mServiceName)) { 221 // The name of the service tells the user what they'd be 222 // connecting to. It could be "Bob's Chat App". 223 Log.d(TAG, "Same machine: " + mServiceName); 224 } else if (service.getServiceName().contains("NsdChat")){ 225 mNsdManager.resolveService(service, mResolveListener); 226 } 227 } 228 229 @Override 230 public void onServiceLost(NsdServiceInfo service) { 231 // When the network service is no longer available. 232 // Internal bookkeeping code goes here. 233 Log.e(TAG, "service lost" + service); 234 } 235 236 @Override 237 public void onDiscoveryStopped(String serviceType) { 238 Log.i(TAG, "Discovery stopped: " + serviceType); 239 } 240 241 @Override 242 public void onStartDiscoveryFailed(String serviceType, int errorCode) { 243 Log.e(TAG, "Discovery failed: Error code:" + errorCode); 244 mNsdManager.stopServiceDiscovery(this); 245 } 246 247 @Override 248 public void onStopDiscoveryFailed(String serviceType, int errorCode) { 249 Log.e(TAG, "Discovery failed: Error code:" + errorCode); 250 mNsdManager.stopServiceDiscovery(this); 251 } 252 }; 253} 254</pre> 255 256<p>The NSD API uses the methods in this interface to inform your application when discovery 257is started, when it fails, and when services are found and lost (lost means "is 258no longer available"). Notice that this snippet does several checks 259when a service is found.</p> 260<ol> 261 <li>The service name of the found service is compared to the service 262name of the local service to determine if the device just picked up its own 263broadcast (which is valid).</li> 264<li>The service type is checked, to verify it's a type of service your 265application can connect to.</li> 266<li>The service name is checked to verify connection to the correct 267application.</li> 268</ol> 269 270<p>Checking the service name isn't always necessary, and is only relevant if you 271want to connect to a specific application. For instance, the application might 272only want to connect to instances of itself running on other devices. However, if the 273application wants to connect to a network printer, it's enough to see that the service type 274is "_ipp._tcp".</p> 275 276<p>After setting up the listener, call {@link android.net.nsd.NsdManager#discoverServices(String, int, 277NsdManager.DiscoveryListener) discoverServices()}, passing in the service type 278your application should look for, the discovery protocol to use, and the 279listener you just created.</p> 280 281<pre> 282 mNsdManager.discoverServices( 283 SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); 284</pre> 285 286 287<h2 id="connect">Connect to Services on the Network</h2> 288<p>When your application finds a service on the network to connect to, it 289must first determine the connection information for that service, using the 290{@link android.net.nsd.NsdManager#resolveService resolveService()} method. 291Implement a {@link android.net.nsd.NsdManager.ResolveListener} to pass into this 292method, and use it to get a {@link android.net.nsd.NsdServiceInfo} containing 293the connection information.</p> 294 295<pre> 296public void initializeResolveListener() { 297 mResolveListener = new NsdManager.ResolveListener() { 298 299 @Override 300 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { 301 // Called when the resolve fails. Use the error code to debug. 302 Log.e(TAG, "Resolve failed" + errorCode); 303 } 304 305 @Override 306 public void onServiceResolved(NsdServiceInfo serviceInfo) { 307 Log.e(TAG, "Resolve Succeeded. " + serviceInfo); 308 309 if (serviceInfo.getServiceName().equals(mServiceName)) { 310 Log.d(TAG, "Same IP."); 311 return; 312 } 313 mService = serviceInfo; 314 int port = mService.getPort(); 315 InetAddress host = mService.getHost(); 316 } 317 }; 318} 319</pre> 320 321<p>Once the service is resolved, your application receives detailed 322service information including an IP address and port number. This is everything 323you need to create your own network connection to the service.</p> 324 325 326<h2 id="teardown">Unregister Your Service on Application Close</h2> 327<p>It's important to enable and disable NSD 328functionality as appropriate during the application's 329lifecycle. Unregistering your application when it closes down helps prevent 330other applications from thinking it's still active and attempting to connect to 331it. Also, service discovery is an expensive operation, and should be stopped 332when the parent Activity is paused, and re-enabled when the Activity is 333resumed. Override the lifecycle methods of your main Activity and insert code 334to start and stop service broadcast and discovery as appropriate.</p> 335 336<pre> 337//In your application's Activity 338 339 @Override 340 protected void onPause() { 341 if (mNsdHelper != null) { 342 mNsdHelper.tearDown(); 343 } 344 super.onPause(); 345 } 346 347 @Override 348 protected void onResume() { 349 super.onResume(); 350 if (mNsdHelper != null) { 351 mNsdHelper.registerService(mConnection.getLocalPort()); 352 mNsdHelper.discoverServices(); 353 } 354 } 355 356 @Override 357 protected void onDestroy() { 358 mNsdHelper.tearDown(); 359 mConnection.tearDown(); 360 super.onDestroy(); 361 } 362 363 // NsdHelper's tearDown method 364 public void tearDown() { 365 mNsdManager.unregisterService(mRegistrationListener); 366 mNsdManager.stopServiceDiscovery(mDiscoveryListener); 367 } 368</pre> 369 370