Ağ hizmeti keşfi (NSD), uygulamanızın yerel bir ağda diğer cihazların sağladığı hizmetlere erişmesini sağlar. NSD'yi destekleyen cihazlar arasında yazıcılar, web kameraları, HTTPS sunucuları ve diğer mobil cihazlar bulunur.
NSD, DNS tabanlı Hizmet Keşfi (DNS-SD) mekanizmasını uygular. Bu mekanizma, uygulamanızın bir hizmet türü ve istenen hizmet türünü sağlayan cihaz örneğinin adını belirterek hizmet istemesine olanak tanır. DNS-SD, hem Android'de hem de diğer mobil platformlarda desteklenir.
Uygulamanıza NSD eklemek, kullanıcılarınızın yerel ağ üzerinde uygulamanızın istediği hizmetleri destekleyen diğer cihazları tanımlamasına olanak tanır. Bu, dosya paylaşma veya çok oyunculu oyun oynama gibi çeşitli eşler arası uygulamalarda kullanışlıdır. Android'in NSD API'leri, bu tür özellikleri uygulamanız için gereken çabayı basitleştirir.
Bu derste, adını ve bağlantı bilgilerini yerel ağa yayınlayabilecek ve aynı işi yapan diğer uygulamaların bilgilerini tarayabilecek bir uygulamanın nasıl oluşturulacağı gösterilmektedir. Son olarak bu derste, başka bir cihazda çalışan aynı uygulamaya nasıl bağlanacağınız gösterilmektedir.
Hizmetinizi ağa kaydetme
Not: Bu adım isteğe bağlıdır. Uygulamanızın hizmetlerini yerel ağ üzerinde yayınlamak istemiyorsanız Ağ'daki Hizmetleri Keşfetme adlı sonraki bölüme geçebilirsiniz.
Hizmetinizi yerel ağa kaydetmek için önce bir NsdServiceInfo
nesnesi oluşturun. Bu nesne, ağdaki diğer cihazların hizmetinize bağlanmaya karar verirken kullandıkları bilgileri sağlar.
Kotlin
fun registerService(port: Int) { // Create the NsdServiceInfo object, and populate it. val serviceInfo = NsdServiceInfo().apply { // The name is subject to change based on conflicts // with other services advertised on the same network. serviceName = "NsdChat" serviceType = "_nsdchat._tcp" setPort(port) ... } }
Java
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("_nsdchat._tcp"); serviceInfo.setPort(port); ... }
Bu kod snippet'i, hizmet adını "NsdChat" olarak ayarlar. Hizmet adı örnek adıdır. Ağdaki diğer cihazlar tarafından görülebilen addır. Ad, yerel hizmetleri aramak için NSD kullanan ağdaki tüm cihazlar tarafından görülebilir. Adın, ağdaki tüm hizmetler için benzersiz olması gerektiğini ve Android'in çakışma çözümlemesini otomatik olarak hallettiğini unutmayın. Ağdaki iki cihazda da NsdChat uygulaması yüklüyse bunlardan biri hizmet adını otomatik olarak "NsdChat"(1)" gibi bir şekilde değiştirir.
İkinci parametre hizmet türünü ayarlayıp uygulamanın kullandığı protokolü ve aktarım katmanını belirtir. Söz dizimi "_<protokol>._<transportlayer>" şeklindedir. Kod snippet'inde hizmet, TCP üzerinden çalışan HTTP protokolünü kullanır. Yazıcı hizmeti (örneğin, bir ağ yazıcısı) sunan bir uygulama, hizmet türünü "_ipp._tcp" olarak ayarlar.
Not: Uluslararası Atanmış Numara Yetkilisi (IANA), NSD ve Bonjour gibi hizmet keşif protokolleri tarafından kullanılan merkezi ve yetkili hizmet türlerinin listesini yönetir. Listeyi IANA hizmet adları ve bağlantı noktası numaraları listesinden indirebilirsiniz. Yeni bir hizmet türü kullanmayı planlıyorsanız IANA Limanları ve Hizmet kayıt formunu doldurarak hizmeti ayırtmanız gerekir.
Hizmetiniz için bağlantı noktasını ayarlarken, diğer uygulamalarla çakışabileceğinden bunu koda gömmekten kaçının. Örneğin, uygulamanızın her zaman 1337 numaralı bağlantı noktasını kullandığını varsayarsak uygulama, aynı bağlantı noktasını kullanan diğer yüklü uygulamalarla olası bir çakışmaya neden olur. Bunun yerine, cihazda bulunan bir sonraki bağlantı noktasını kullanın. Bu bilgiler diğer uygulamalara bir hizmet yayını tarafından sağlandığından, uygulamanızın derleme sırasında diğer uygulamalar tarafından bilinmesi için bağlantı noktasına gerek yoktur. Bunun yerine, uygulamalar bu bilgileri hizmetinize bağlanmadan hemen önce hizmet yayınınızdan alabilir.
Yuvalarla çalışıyorsanız bir yuvayı 0'a ayarlayarak kullanılabilir herhangi bir bağlantı noktasına nasıl başlatabileceğiniz aşağıda açıklanmıştır.
Kotlin
fun initializeServerSocket() { // Initialize a server socket on the next available port. serverSocket = ServerSocket(0).also { socket -> // Store the chosen port. mLocalPort = socket.localPort ... } }
Java
public void initializeServerSocket() { // Initialize a server socket on the next available port. serverSocket = new ServerSocket(0); // Store the chosen port. localPort = serverSocket.getLocalPort(); ... }
NsdServiceInfo
nesnesini tanımladığınıza göre artık RegistrationListener
arayüzünü uygulamanız gerekir. Bu arayüzde, hizmet kaydının başarılı veya başarısız olduğu konusunda uygulamanızı uyarmak amacıyla Android tarafından kullanılan geri çağırmalar yer alır.
Kotlin
private val registrationListener = object : NsdManager.RegistrationListener { override fun 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.serviceName } override fun onRegistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { // Registration failed! Put debugging code here to determine why. } override fun onServiceUnregistered(arg0: NsdServiceInfo) { // Service has been unregistered. This only happens when you call // NsdManager.unregisterService() and pass in this listener. } override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { // Unregistration failed. Put debugging code here to determine why. } }
Java
public void initializeRegistrationListener() { registrationListener = 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. serviceName = 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. } }; }
Artık hizmetinizi kaydetmek için gereken tüm parçalara sahipsiniz. registerService()
yöntemini çağırın.
Bu yöntemin eşzamansız olduğunu, bu nedenle hizmet kaydedildikten sonra çalışması gereken tüm kodların onServiceRegistered()
yöntemine gitmesi gerektiğini unutmayın.
Kotlin
fun registerService(port: Int) { // Create the NsdServiceInfo object, and populate it. val serviceInfo = NsdServiceInfo().apply { // The name is subject to change based on conflicts // with other services advertised on the same network. serviceName = "NsdChat" serviceType = "_nsdchat._tcp" setPort(port) } nsdManager = (getSystemService(Context.NSD_SERVICE) as NsdManager).apply { registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener) } }
Java
public void registerService(int port) { NsdServiceInfo serviceInfo = new NsdServiceInfo(); serviceInfo.setServiceName("NsdChat"); serviceInfo.setServiceType("_http._tcp."); serviceInfo.setPort(port); nsdManager = Context.getSystemService(Context.NSD_SERVICE); nsdManager.registerService( serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener); }
Ağdaki hizmetleri keşfedin
Ağ, canavar ağ yazıcılarından dost web kameralarına ve yakındaki tik-tac-toe oyuncularının acımasız, acımasız savaşlarına kadar hayatla dolu. Uygulamanızın bu canlı işlev ekosistemini görmesini sağlamanın anahtarı hizmet keşfidir. Uygulamanızın, hangi hizmetlerin kullanılabileceğini görmek için ağdaki hizmet yayınlarını dinlemesi ve uygulamanın birlikte çalışamayacağı tüm öğeleri filtrelemesi gerekir.
Hizmet kaydı gibi hizmet keşfi de iki adımdan oluşur: alakalı geri çağırmalara sahip bir keşif dinleyicisi ayarlama ve discoverServices()
için tek bir eşzamansız API çağrısı yapma.
Öncelikle, NsdManager.DiscoveryListener
yöntemini uygulayan bir anonim sınıf örneği oluşturun. Aşağıdaki snippet'te basit bir örnek gösterilmektedir:
Kotlin
// Instantiate a new DiscoveryListener private val discoveryListener = object : NsdManager.DiscoveryListener { // Called as soon as service discovery begins. override fun onDiscoveryStarted(regType: String) { Log.d(TAG, "Service discovery started") } override fun onServiceFound(service: NsdServiceInfo) { // A service was found! Do something with it. Log.d(TAG, "Service discovery success$service") when { service.serviceType != SERVICE_TYPE -> // Service type is the string containing the protocol and // transport layer for this service. Log.d(TAG, "Unknown Service Type: ${service.serviceType}") service.serviceName == 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") service.serviceName.contains("NsdChat") -> nsdManager.resolveService(service, resolveListener) } } override fun onServiceLost(service: NsdServiceInfo) { // When the network service is no longer available. // Internal bookkeeping code goes here. Log.e(TAG, "service lost: $service") } override fun onDiscoveryStopped(serviceType: String) { Log.i(TAG, "Discovery stopped: $serviceType") } override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) { Log.e(TAG, "Discovery failed: Error code:$errorCode") nsdManager.stopServiceDiscovery(this) } override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) { Log.e(TAG, "Discovery failed: Error code:$errorCode") nsdManager.stopServiceDiscovery(this) } }
Java
public void initializeDiscoveryListener() { // Instantiate a new DiscoveryListener discoveryListener = 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(serviceName)) { // 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: " + serviceName); } else if (service.getServiceName().contains("NsdChat")){ nsdManager.resolveService(service, resolveListener); } } @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); nsdManager.stopServiceDiscovery(this); } @Override public void onStopDiscoveryFailed(String serviceType, int errorCode) { Log.e(TAG, "Discovery failed: Error code:" + errorCode); nsdManager.stopServiceDiscovery(this); } }; }
NSD API, uygulamanızı keşif işleminin ne zaman başlayacağı, ne zaman başarısız olduğu ve hizmetlerin ne zaman bulunup kaybedildiği (kayıp) ("artık kullanılamıyor") hakkında bilgilendirmek için bu arayüzdeki yöntemleri kullanır. Bir hizmet bulunduğunda bu snippet'in birkaç kontrol yaptığına dikkat edin.
- Bulunan hizmetin hizmet adı, cihazın kısa süre önce kendi yayınını (geçerlidir) alıp almadığını belirlemek için yerel hizmetin hizmet adıyla karşılaştırılır.
- Hizmet türü, uygulamanızın bağlanabileceği bir hizmet türü olduğunu doğrulamak için kontrol edilir.
- Doğru uygulamayla olan bağlantıyı doğrulamak için hizmet adı kontrol edilir.
Hizmet adının kontrol edilmesi her zaman gerekli değildir ve yalnızca belirli bir uygulamaya bağlanmak istediğinizde kullanılabilir. Örneğin, uygulama yalnızca başka cihazlarda çalışan kendi örneklerine bağlanmak isteyebilir. Ancak uygulama bir ağ yazıcısına bağlanmak isterse hizmet türünün "_ipp._tcp" olduğunu görmeniz yeterlidir.
İşleyiciyi ayarladıktan sonra discoverServices()
yöntemini çağırın, uygulamanızın araması gereken hizmet türünü, kullanılacak keşif protokolünü ve az önce oluşturduğunuz işleyiciyi iletin.
Kotlin
nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
Java
nsdManager.discoverServices( SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener);
Ağdaki hizmetlere bağlanma
Uygulamanız bağlanmak için ağda bir hizmet bulduğunda, önce resolveService()
yöntemini kullanarak bu hizmetin bağlantı bilgilerini belirlemelidir.
Bu yönteme iletmek için bir NsdManager.ResolveListener
uygulayın ve bağlantı bilgilerini içeren bir NsdServiceInfo
elde etmek için bunu kullanın.
Kotlin
private val resolveListener = object : NsdManager.ResolveListener { override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { // Called when the resolve fails. Use the error code to debug. Log.e(TAG, "Resolve failed: $errorCode") } override fun onServiceResolved(serviceInfo: NsdServiceInfo) { Log.e(TAG, "Resolve Succeeded. $serviceInfo") if (serviceInfo.serviceName == mServiceName) { Log.d(TAG, "Same IP.") return } mService = serviceInfo val port: Int = serviceInfo.port val host: InetAddress = serviceInfo.host } }
Java
public void initializeResolveListener() { resolveListener = 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(serviceName)) { Log.d(TAG, "Same IP."); return; } mService = serviceInfo; int port = mService.getPort(); InetAddress host = mService.getHost(); } }; }
Hizmet çözümlendikten sonra uygulamanız, IP adresi ve bağlantı noktası numarası da dahil olmak üzere ayrıntılı hizmet bilgileri alır. Hizmetle kendi ağ bağlantınızı oluşturmanız için ihtiyacınız olan her şey bunlardır.
Başvuru kapatıldığında hizmetinizin kaydını iptal edin
Uygulamanın yaşam döngüsü boyunca NSD işlevini gerektiği şekilde etkinleştirmek ve devre dışı bırakmak önemlidir. Uygulama kapatıldığında kaydını iptal etmek, diğer uygulamaların hâlâ etkin olduğunu ve uygulamaya bağlanmaya çalıştığını düşünmesini önlemeye yardımcı olur. Ayrıca hizmet keşfi pahalı bir işlemdir ve üst Etkinlik duraklatıldığında durdurulmalı ve Etkinlik devam ettirildiğinde yeniden etkinleştirilmelidir. Ana Etkinliğinizin yaşam döngüsü yöntemlerini geçersiz kılın ve hizmet yayınını ve keşfini uygun şekilde başlatmak ve durdurmak için kod ekleyin.
Kotlin
// In your application's Activity override fun onPause() { nsdHelper?.tearDown() super.onPause() } override fun onResume() { super.onResume() nsdHelper?.apply { registerService(connection.localPort) discoverServices() } } override fun onDestroy() { nsdHelper?.tearDown() connection.tearDown() super.onDestroy() } // NsdHelper's tearDown method fun tearDown() { nsdManager.apply { unregisterService(registrationListener) stopServiceDiscovery(discoveryListener) } }
Java
// In your application's Activity @Override protected void onPause() { if (nsdHelper != null) { nsdHelper.tearDown(); } super.onPause(); } @Override protected void onResume() { super.onResume(); if (nsdHelper != null) { nsdHelper.registerService(connection.getLocalPort()); nsdHelper.discoverServices(); } } @Override protected void onDestroy() { nsdHelper.tearDown(); connection.tearDown(); super.onDestroy(); } // NsdHelper's tearDown method public void tearDown() { nsdManager.unregisterService(registrationListener); nsdManager.stopServiceDiscovery(discoveryListener); }