1 page.title=Using Network Service Discovery 2 3 trainingnavtop=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 37 identify other devices on the local network that support the services your app 38 requests. This is useful for a variety of peer-to-peer applications such as file 39 sharing or multi-player gaming. Android's NSD APIs simplify the effort required 40 for you to implement such features.</p> 41 42 <p>This lesson shows you how to build an application that can broadcast its 43 name and connection information to the local network and scan for information 44 from other applications doing the same. Finally, this lesson shows you how 45 to 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 50 you don't care about broadcasting your app's services over the local network, 51 you can skip forward to the 52 next 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 55 android.net.nsd.NsdServiceInfo} object. This object provides the information 56 that other devices on the network use when they're deciding whether to connect to your 57 service. </p> 58 59 <pre> 60 public 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". 74 The name is visible to any device on the network that is using NSD to look for 75 local services. Keep in mind that the name must be unique for any service on the 76 network, and Android automatically handles conflict resolution. If 77 two devices on the network both have the NsdChat application installed, one of 78 them 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 82 layer the application uses. The syntax is 83 "_<protocol>._<transportlayer>". In the 84 code snippet, the service uses HTTP protocol running over TCP. An application 85 offering a printer service (for instance, a network printer) would set the 86 service type to "_ipp._tcp".</p> 87 88 <p class="note"><strong>Note: </strong> The International Assigned Numbers 89 Authority (IANA) manages a centralized, 90 authoritative list of service types used by service discovery protocols such as NSD and Bonjour. 91 You can download the list from <a 92 href="http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml">the 93 IANA list of service names and port numbers</a>. 94 If you intend to use a new service type, you should reserve it by filling out 95 the <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 100 conflicts with other applications. For instance, assuming 101 that your application always uses port 1337 puts it in potential conflict with 102 other installed applications that use the same port. Instead, use the device's 103 next available port. Because this information is provided to other apps by a 104 service broadcast, there's no need for the port your application uses to be 105 known by other applications at compile-time. Instead, the applications can get 106 this information from your service broadcast, right before connecting to your 107 service.</p> 108 109 <p>If you're working with sockets, here's how you can initialize a socket to any 110 available port simply by setting it to 0.</p> 111 112 <pre> 113 public 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 124 NsdServiceInfo} object, you need to implement the {@link 125 android.net.nsd.NsdManager.RegistrationListener RegistrationListener} interface. This 126 interface contains callbacks used by Android to alert your application of the 127 success or failure of service registration and unregistration. 128 </p> 129 <pre> 130 public 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 165 after the service has been registered must go in the {@link 166 android.net.nsd.NsdManager.RegistrationListener#onServiceRegistered(NsdServiceInfo) 167 onServiceRegistered()} method.</p> 168 169 <pre> 170 public 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 185 docile network webcams, to the brutal, fiery battles of nearby tic-tac-toe 186 players. The key to letting your application see this vibrant ecosystem of 187 functionality is service discovery. Your application needs to listen to service 188 broadcasts on the network to see what services are available, and filter out 189 anything 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 193 API 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 197 android.net.nsd.NsdManager.DiscoveryListener}. The following snippet shows a 198 simple example:</p> 199 200 <pre> 201 public 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 257 is started, when it fails, and when services are found and lost (lost means "is 258 no longer available"). Notice that this snippet does several checks 259 when a service is found.</p> 260 <ol> 261 <li>The service name of the found service is compared to the service 262 name of the local service to determine if the device just picked up its own 263 broadcast (which is valid).</li> 264 <li>The service type is checked, to verify it's a type of service your 265 application can connect to.</li> 266 <li>The service name is checked to verify connection to the correct 267 application.</li> 268 </ol> 269 270 <p>Checking the service name isn't always necessary, and is only relevant if you 271 want to connect to a specific application. For instance, the application might 272 only want to connect to instances of itself running on other devices. However, if the 273 application wants to connect to a network printer, it's enough to see that the service type 274 is "_ipp._tcp".</p> 275 276 <p>After setting up the listener, call {@link android.net.nsd.NsdManager#discoverServices(String, int, 277 NsdManager.DiscoveryListener) discoverServices()}, passing in the service type 278 your application should look for, the discovery protocol to use, and the 279 listener 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 289 must first determine the connection information for that service, using the 290 {@link android.net.nsd.NsdManager#resolveService resolveService()} method. 291 Implement a {@link android.net.nsd.NsdManager.ResolveListener} to pass into this 292 method, and use it to get a {@link android.net.nsd.NsdServiceInfo} containing 293 the connection information.</p> 294 295 <pre> 296 public 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 322 service information including an IP address and port number. This is everything 323 you 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 328 functionality as appropriate during the application's 329 lifecycle. Unregistering your application when it closes down helps prevent 330 other applications from thinking it's still active and attempting to connect to 331 it. Also, service discovery is an expensive operation, and should be stopped 332 when the parent Activity is paused, and re-enabled when the Activity is 333 resumed. Override the lifecycle methods of your main Activity and insert code 334 to 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