Utiliser la détection de services réseau

Network Service Discovery (NSD) permet à votre application d'accéder à des services appareils fournissent sur un réseau local. Les appareils compatibles avec NSD incluent les imprimantes, les webcams, les serveurs HTTPS et autres appareils mobiles.

NSD implémente le mécanisme de recherche de services basé sur DNS (DNS-SD), qui permet à votre application de demander des services en spécifiant un type de service et le nom d'une instance d'appareil qui fournit le type de service souhaité. DNS-SD est pris en charge à la fois sur Android et sur d'autres plateformes mobiles.

L'ajout d'un fichier NSD à votre application permet à vos utilisateurs d'identifier d'autres appareils sur le réseau local qui prend en charge les services demandés par votre application. Ceci est utile pour Diverses applications peer-to-peer, telles que le partage de fichiers ou le mode multijoueur les jeux vidéo. Les API NSD d'Android simplifient l'implémentation ces caractéristiques.

Cette leçon vous explique comment créer une application capable de diffuser le nom et les informations de connexion au réseau local, puis recherchez des informations à partir d'autres applications faisant de même. Enfin, cette leçon vous montre comment pour se connecter à la même application s'exécutant sur un autre appareil.

Enregistrer votre service sur le réseau

Remarque : Cette étape est facultative. Si vous ne vous souciez pas de diffuser les services de votre application sur le réseau local, vous pouvez passer à la section dans la section suivante, Découvrir les services sur le réseau.

Pour enregistrer votre service sur le réseau local, créez d'abord un objet NsdServiceInfo. Cet objet fournit les informations utilisés par les autres appareils du réseau lorsqu'ils se connectent à votre Google Cloud.

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

Cet extrait de code définit le nom du service sur "NsdChat". Nom du service est le nom de l'instance: c'est le nom que les autres appareils du réseau peuvent voir. Le nom est visible par tout appareil du réseau qui utilise NSD pour rechercher Local Services. Gardez à l'esprit que le nom doit être unique pour chaque service sur le réseau, et Android gère automatiquement la résolution des conflits. Si l'application NsdChat est installée sur deux appareils du réseau, le nom du service est automatiquement remplacé par "NsdChat". (1)".

Le deuxième paramètre définit le type de service, spécifie le protocole et le protocole de transport la couche de stockage utilisée par l'application. La syntaxe est "_<protocole>._<transportlayer>". Dans extrait de code, le service utilise le protocole HTTP s'exécutant sur TCP. Une application en proposant un service d'impression (par exemple, une imprimante réseau) définirait le type de service sur "_ipp._tcp".

Remarque : L'onglet "International Assigned Numbers" (Numéros attribués internationaux) de l'IANA (IANA) gère un système de gestion liste primaire des types de services utilisés par les protocoles de détection de services tels que NSD et Bonjour. Vous pouvez télécharger cette liste à partir de l' Liste IANA des noms de services et des numéros de port Si vous avez l'intention d'utiliser un nouveau type de service, nous vous conseillons de le réserver en remplissant les ports et services IANA formulaire d'inscription.

Lorsque vous définissez le port de votre service, évitez de le coder en dur, entre en conflit avec d'autres applications. Par exemple, en supposant que que votre application utilise toujours le port 1337, elle risque d'être en conflit avec autres applications installées qui utilisent le même port. Utilisez plutôt le le prochain port disponible. Ces informations étant fournies à d'autres applications par un diffusion de service, il est inutile que le port utilisé par votre application soit connus par d'autres applications au moment de la compilation. Au lieu de cela, les applications peuvent ces informations provenant de votre service diffusé, juste avant de vous connecter Google Cloud.

Si vous travaillez avec des sockets, voici comment les initialiser sur disponible en le définissant simplement sur 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();
    ...
}

Maintenant que vous avez défini l'objet NsdServiceInfo, vous devez implémenter l'interface RegistrationListener. Ce contient des rappels utilisés par Android pour alerter votre application du la réussite ou l'échec de l'enregistrement et de l'annulation de l'enregistrement du service.

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

Vous disposez maintenant de tous les éléments nécessaires pour enregistrer votre service. Appeler la méthode registerService()

Notez que cette méthode est asynchrone, de sorte que tout code devant être exécuté après l'enregistrement du service, vous devez utiliser la méthode 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);
}

Découvrir des services sur le réseau

Le réseau déborde de vie, des imprimantes réseau de ces monstres aux webcams docile réseau, aux batailles brutales et ardentes des jeux de morpion à proximité joueurs. La clé pour permettre à votre application de profiter de cet écosystème dynamique est la détection de services. Votre application doit écouter le service sur le réseau pour voir les services disponibles et filtrer tout ce avec quoi l’application ne peut pas fonctionner.

La détection de services, comme l'enregistrement d'un service, comporte deux étapes: en configurant un écouteur de découverte avec les rappels appropriés et en effectuant une seule opération asynchrone Appel d'API à discoverServices().

Commencez par instancier une classe anonyme qui implémente NsdManager.DiscoveryListener. L'extrait de code suivant montre exemple simple:

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

L'API NSD utilise les méthodes de cette interface pour informer votre application lors de la découverte est démarré, lorsqu'il échoue, et lorsque des services sont trouvés et perdus (perdu signifie « est ne sont plus disponibles"). Notez que cet extrait effectue plusieurs vérifications lorsqu'un service est trouvé.

  1. Le nom du service trouvé est comparé au nom du service nom du service local pour déterminer si l'appareil vient de récupérer diffusion (ce qui est valide).
  2. Le type de service est coché. Pour s'assurer qu'il s'agit d'un type de service auquel l'application peut se connecter.
  3. Le nom du service est vérifié pour s'assurer qu'il est bien connecté au bon application.

Il n'est pas toujours nécessaire de vérifier le nom du service. Cela n'est pertinent que si vous veulent se connecter à une application spécifique. Par exemple, l'application peut veulent uniquement se connecter à des instances de lui-même exécutées sur d’autres appareils. Toutefois, si le demande à se connecter à une imprimante réseau, il suffit de voir que le type de service est "_ipp._tcp".

Après avoir configuré l'écouteur, appelez discoverServices() en transmettant le type de service. votre application doit rechercher, le protocole de découverte à utiliser que vous venez de créer.

Kotlin

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

Java

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

Se connecter aux services du réseau

Lorsque votre application trouve un service sur le réseau auquel se connecter, elle doit d'abord déterminer les informations de connexion de ce service, à l'aide du resolveService(). Implémentez un NsdManager.ResolveListener à transmettre à ce et l'utiliser pour obtenir un NsdServiceInfo contenant les informations de connexion.

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

Une fois le service résolu, votre application reçoit des informations détaillées des informations de service, y compris une adresse IP et un numéro de port. C'est tout vous devez créer votre propre connexion réseau au service.

Annuler l'enregistrement de votre service à la fermeture de l'application

Il est important d'activer et de désactiver NSD des fonctionnalités selon les besoins tout au long du cycle de vie. Le fait d'annuler l'enregistrement de votre application à sa fermeture permet d'éviter d'autres applications de penser qu'elles sont toujours actives et de tenter de se connecter De plus, la détection de services est une opération coûteuse et doit être arrêtée lorsque l'activité parent est mise en veille et réactivée lorsque l'activité est réactivée(s) Remplacer les méthodes de cycle de vie de votre activité principale et insérer du code pour démarrer et arrêter la diffusion et la détection du service, le cas échéant.

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