Użyj wykrywania usług sieciowych

Wykrywanie usług sieciowych (NSD) zapewnia aplikacji dostęp do usług, które inne urządzeń z sieci lokalnej. Urządzenia obsługujące NSD to drukarki, przez kamery internetowe, serwery HTTPS i inne urządzenia mobilne.

NSD wdraża oparty na DNS mechanizm wykrywania usług (DNS-SD), który umożliwia aplikacji żądania usług przez określenie typu usługi i jej nazwy instancji urządzenia, która udostępnia pożądany typ usługi. DNS-SD to zarówno na Androidzie, jak i na innych platformach mobilnych.

Dodanie NSD do aplikacji umożliwia użytkownikom identyfikowanie innych urządzeń przez sieć lokalną obsługującą usługi, o które prosi Twoja aplikacja. Jest to przydatne w przypadku różne aplikacje peer-to-peer, np. do udostępniania plików lub gry wieloosobowe, w grach komputerowych. Interfejsy API NSD na Androida upraszczają implementację tych funkcji.

Z tej lekcji dowiesz się, jak utworzyć aplikację, która może przesyłać nazwę i informacje o połączeniu z siecią lokalną oraz skanowanie w poszukiwaniu informacji niż inne aplikacje. Z tej lekcji dowiesz się, , aby połączyć się z tą samą aplikacją działającą na innym urządzeniu.

Rejestrowanie usługi w sieci

Uwaga: ten krok jest opcjonalny. Jeśli nie zależy Ci na transmitowaniu usług aplikacji przez sieć lokalną, możesz przewinąć do w następnej sekcji – Znajdowanie usług w sieci.

Aby zarejestrować usługę w sieci lokalnej, najpierw utwórz obiekt NsdServiceInfo. Ten obiekt dostarcza informacje używane przez inne urządzenia w sieci podczas podejmowania decyzji o połączeniu posprzedażna.

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

Ten fragment kodu ustawia nazwę usługi na „NsdChat”. Nazwa usługi to nazwa instancji, która jest widoczna dla innych urządzeń w sieci. Ta nazwa jest widoczna dla każdego urządzenia w sieci korzystającej z NSD do wyszukiwania i usług lokalnych. Pamiętaj, że nazwa musi być niepowtarzalna dla każdej usługi w danym kraju sieci, a Android automatycznie rozwiązuje konflikty. Jeśli na dwóch urządzeniach w sieci jest zainstalowana aplikacja NsdChat, jedno z automatycznie zmieni nazwę usługi na coś w rodzaju „NsdChat” (1)”.

Drugi parametr określa typ usługi, czyli protokół i transport i warstwy używanej przez aplikację. Składnia "_<protokół>._<transportlayer>". W fragment kodu, usługa wykorzystuje protokół HTTP działający przez TCP. Aplikacja oferowanie usługi drukarki (np. drukarki sieciowej) spowoduje ustawienie typ usługi na „_ipp._tcp”.

Uwaga: międzynarodowe numery przypisane Agencja (IANA) zarządza scentralizowanym, autorytatywna lista typów usług używanych przez protokoły wykrywania usług, takie jak NSD i Bonjour. Możesz pobrać listę z Lista nazw usług i numerów portów IANA. Jeśli chcesz korzystać z nowego typu usługi, zarezerwuj go, wypełniając IANA Ports and Service formularza rejestracyjnego.

Podczas ustawiania portu dla usługi unikaj kodowania na stałe, ponieważ powoduje konflikty z innymi aplikacjami. Na przykład zakładając, że aplikacja zawsze używa portu 1337, co powoduje konflikt z innych zainstalowanych aplikacji, które korzystają z tego samego portu. Zamiast tego użyj kolejny dostępny port. Informacje te są przekazywane innym aplikacjom przez komunikat usługi, nie musi być port, którego używa Twoja aplikacja znane innym aplikacjom podczas kompilowania. Zamiast tego aplikacje mogą otrzymywać informacji pochodzących z transmisji usługi, bezpośrednio przed połączeniem posprzedażna.

Aby zainicjować gniazdo w dowolnym przez ustawienie tej wartości na 0.

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();
    ...
}

Po zdefiniowaniu obiektu NsdServiceInfo musisz wdrożyć interfejs RegistrationListener. Ten zawiera wywołania zwrotne używane przez Androida do alertów aplikacji o udanej lub nieudanej rejestracji w usłudze albo jej wyrejestrowania.

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.
        }
    };
}

Masz już wszystkie elementy niezbędne do zarejestrowania usługi. Wywołaj metodę registerService()

Pamiętaj, że ta metoda jest asynchroniczna, więc po uruchomieniu każdego kodu po zarejestrowaniu usługi musi być stosowana metoda onServiceRegistered().

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

Wykrywaj usługi w sieci

Sieć kwitnie, od upiornych drukarek sieciowych po pogodnych kamer internetowych, aż po brutalne, ogniste bitwy w pobliżu kółków i krzyżyków. graczy. Kluczem do tego, aby aplikacja mogła zobaczyć ten tętniący życiem ekosystem jest wykrywanie usług. Aplikacja musi nasłuchiwać usługi transmituje w sieci, sprawdza dostępne usługi i odfiltrowuje wszystko, czego aplikacja nie może obsłużyć.

Wykrywanie usług, tak jak rejestracja usługi, składa się z 2 etapów: skonfigurować detektor wykrywania z odpowiednimi wywołaniami zwrotnymi i utworzyć jeden asynchroniczny kod Wywołanie discoverServices() przez interfejs API.

Najpierw utwórz instancję anonimowej klasy, która implementuje NsdManager.DiscoveryListener. Ten fragment kodu zawiera prosty przykład:

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

Interfejs NSD API wykorzystuje metody w tym interfejsie, aby informować aplikację o wykrywaniu uruchomione, gdy nie działa, oraz gdy usługi są znalezione i utracone (zagubienie oznacza „to nie są już dostępne”). Zwróć uwagę, że ten fragment zawiera kilka testów po znalezieniu usługi.

  1. Nazwa znalezionej usługi jest porównywana z nazwą usługi nazwa usługi lokalnej, aby określić, czy urządzenie właśnie odebrało swoje (która jest prawidłowa).
  2. typ usługi jest sprawdzany, aby zweryfikować, czy jest to typ usługi; może się połączyć z aplikacją.
  3. Nazwa usługi jest sprawdzana w celu zweryfikowania połączenia z właściwym aplikacji.

Sprawdzanie nazwy usługi nie jest konieczne i jest przydatne tylko wtedy, połączyć się z konkretną aplikacją. Aplikacja może na przykład chcą łączyć się tylko z instancjami swojego konta uruchomionymi na innych urządzeniach. Jeśli jednak aplikacja chce połączyć się z drukarką sieciową, wystarczy, że wykryje, że typ usługi to „_ipp._tcp”.

Po skonfigurowaniu odbiornika wywołaj discoverServices(), przekazując typ usługi których aplikacja powinna szukać, jaki protokół wykrywania ma zastosować utworzony przez Ciebie słuchacz.

Kotlin

nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)

Java

nsdManager.discoverServices(
        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener);

Łączenie z usługami w sieci

Gdy aplikacja znajdzie w sieci usługę, z którą będzie mogła się połączyć, musi najpierw określić informacje o połączeniu dla danej usługi, korzystając z Metoda resolveService(). Zaimplementuj NsdManager.ResolveListener, aby to przekazywać i użyj go, aby uzyskać NsdServiceInfo zawierającą informacje o połączeniu.

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

Po rozwiązaniu problemu z usługą Twoja aplikacja otrzyma szczegółowe informacje informacje o usłudze, w tym adres IP i numer portu. To wszystko musisz utworzyć własne połączenie sieciowe z usługą.

Wyrejestruj usługę po zamknięciu zgłoszenia

Musisz włączyć i wyłączyć NSD odpowiednie funkcje podczas cyklu życia usługi. Wyrejestrowanie aplikacji po zamknięciu pomaga zapobiegać przez inne aplikacje, że są nadal aktywne, i próbują się połączyć . Poza tym wykrywanie usług jest kosztowną operację i należy ją zatrzymać gdy aktywność rodzica jest wstrzymana i włączona, gdy aktywność jest wznowiona. Zastąp metody cyklu życia głównej aktywności i wstaw kod aby odpowiednio rozpocząć i zatrzymać transmisję i wykrywanie usługi.

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