Ağ hizmeti keşfini kullanma

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.

  1. 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.
  2. Hizmet türü, uygulamanızın bağlanabileceği bir hizmet türü olduğunu doğrulamak için kontrol edilir.
  3. 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);
    }