Netzwerkdiensterkennung verwenden

Durch Network Service Discovery (NSD) erhält Ihre App Zugriff auf Dienste, die Geräte in einem lokalen Netzwerk bereitstellen. Zu den Geräten, die NSD unterstützen, gehören Drucker, Webcams, HTTPS-Server und andere Mobilgeräte.

Die NSD implementiert den DNS-basierten Diensterkennungsmechanismus (DNS-SD), der ermöglicht Ihrer Anwendung, Dienste anzufordern, indem Sie einen Diensttyp und den Namen angeben einer Geräteinstanz, die den gewünschten Diensttyp bereitstellt. DNS-SD ist die sowohl auf Android-Geräten als auch auf anderen mobilen Plattformen unterstützt werden.

Wenn Sie Ihrer Anwendung eine NSD hinzufügen, können Ihre Nutzer andere Geräte auf der lokalen Netzwerk, das die von deiner App angeforderten Dienste unterstützt. Dies ist nützlich für Eine Vielzahl von Peer-to-Peer-Anwendungen wie Dateifreigabe oder Multiplayer-Modus Gaming. Die NSD APIs von Android vereinfachen die Implementierung für diese Funktionen.

In dieser Lektion erfahren Sie, wie Sie eine Anwendung erstellen, die und suchen Sie nach Informationen. von anderen Anwendungen, die dasselbe tun. Schließlich erfahren Sie in dieser Lektion, um eine Verbindung zu derselben Anwendung herzustellen, die auf einem anderen Gerät ausgeführt wird.

Dienst im Netzwerk registrieren

Hinweis : Dieser Schritt ist optional. Wenn die App-Dienste nicht über das lokale Netzwerk zu übertragen, können Sie direkt zur im nächsten Abschnitt: Dienste im Netzwerk finden.

Erstellen Sie zuerst ein NsdServiceInfo-Objekt, um Ihren Dienst im lokalen Netzwerk zu registrieren. Dieses Objekt stellt die Informationen bereit, die andere Geräte im Netzwerk nutzen, wenn sie entscheiden, ob sie sich mit deinem .

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

Dieses Code-Snippet legt den Dienstnamen auf „NsdChat“ fest. Dienstname ist der Instanzname. Er ist der für andere Geräte im Netzwerk sichtbare Name. Der Name ist für jedes Gerät im Netzwerk sichtbar, das NSD verwendet, um nach lokale Dienstleistungen. Beachten Sie, dass der Name für jeden Dienst auf der und Android übernimmt die Konfliktlösung automatisch. Wenn Auf zwei Geräten im Netzwerk ist NsdChat installiert, eines der wird der Dienstname automatisch geändert, z. B. in „NsdChat“. (1)" beginnen.

Der zweite Parameter legt den Diensttyp fest und gibt das Protokoll und den Transport an. Ebene, die die Anwendung verwendet. Die Syntax lautet "_<Protokoll>._<Transportschicht>". Im Code-Snippet enthält, verwendet der Dienst ein HTTP-Protokoll, das über TCP ausgeführt wird. Eine Anwendung Wenn Sie einen Druckerdienst (z. B. einen Netzwerkdrucker) anbieten, wird der Diensttyp auf "_ipp._tcp" festlegen.

Hinweis : Die internationalen Zugewiesenen Nummern Die Behörde (IANA) verwaltet eine zentrale, autoritative Liste von Diensttypen, die von Diensterkennungsprotokollen wie NSD und Bonjour verwendet werden. Sie können die Liste über die IANA-Liste mit Dienstnamen und Portnummern. Wenn Sie einen neuen Diensttyp verwenden möchten, sollten Sie ihn reservieren, indem Sie IANA Ports and Service Registrierungsformular.

Vermeiden Sie es beim Festlegen des Ports für Ihren Dienst, ihn so hartzucodieren: in Konflikt mit anderen Anwendungen. Angenommen, Sie dass Ihre Anwendung immer Port 1337 verwendet, kann dies zu einem Konflikt mit Port 1337 führen. anderen installierten Anwendungen, die denselben Port verwenden. Verwenden Sie stattdessen die nächsten verfügbaren Port. Da diese Informationen von einem Dienst-Broadcast zu empfangen, ist es nicht erforderlich, dass der von Ihrer Anwendung verwendete Port Anwendungen bei der Kompilierung bekannt. Stattdessen können die Anwendungen erhalten Sie diese Informationen direkt, bevor Sie eine Verbindung zu Ihrem .

Wenn Sie mit Sockets arbeiten, können Sie wie folgt einen Socket initialisieren indem Sie den Wert auf 0 setzen.

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

Nachdem Sie das NsdServiceInfo-Objekt definiert haben, müssen Sie die RegistrationListener-Schnittstelle implementieren. Dieses enthält Callbacks, die Android verwendet, um Ihre Anwendung des Erfolg oder Misserfolg der Dienstregistrierung und -abmeldung.

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

Jetzt haben Sie alle Elemente, um Ihre Dienstleistung zu registrieren. Methode aufrufen registerService()

Diese Methode ist asynchron. Code, der ausgeführt werden muss, nachdem der Dienst registriert wurde, muss die Methode onServiceRegistered() verwenden.

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

Dienste im Netzwerk entdecken

Im Netzwerk wimmelt es nur so vor Leben, von den finsteren Netzwerkdruckern bis hin zu gedämpfte Netzwerk-Webcams bis hin zu den brutalen, wilden Kämpfen von Tic-Tac-Toe in der Nähe . Der Schlüssel, damit Ihre Anwendung dieses dynamische ist die Diensterkennung. Ihre Anwendung muss den Dienst überwachen Nachrichten an das Netzwerk senden, um zu sehen, welche Dienste verfügbar sind, und mit denen die Anwendung nicht funktionieren kann.

Die Diensterkennung, wie die Dienstregistrierung, umfasst zwei Schritte: indem Sie einen Erkennungs-Listener mit den relevanten Callbacks einrichten und eine einzelne asynchrone API-Aufruf an discoverServices().

Instanziieren Sie zuerst eine anonyme Klasse, die NsdManager.DiscoveryListener implementiert. Das folgende Snippet zeigt eine einfaches Beispiel:

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

Die NSD API verwendet die Methoden in dieser Schnittstelle, um Ihre Anwendung über die Erkennung zu informieren gestartet wird, wenn er fehlschlägt und wenn Dienste gefunden und verloren werden (verloren bedeutet, nicht mehr verfügbar“). Dieses Snippet führt mehrere Prüfungen durch, wenn ein Dienst gefunden wird.

  1. Der Dienstname des gefundenen Dienstes wird mit dem Dienstnamen verglichen Name des lokalen Dienstes, um festzustellen, ob das Gerät gerade seinen eigenen abgerufen hat Broadcast (gültig ist).
  2. Der Diensttyp wurde überprüft, um zu bestätigen, dass es sich um App eine Verbindung herstellen kann.
  3. Der Dienstname wird überprüft, um die Verbindung zum richtigen .

Die Überprüfung des Dienstnamens ist nicht immer erforderlich und nur relevant, wenn Sie eine Verbindung zu einer bestimmten Anwendung herstellen möchten. Zum Beispiel könnte die Anwendung nur eine Verbindung zu eigenen Instanzen herstellen möchte, die auf anderen Geräten ausgeführt werden. Wenn die Anwendung eine Verbindung zu einem Netzwerkdrucker herstellen möchte, reicht es aus, dass der Diensttyp ist „_ipp._tcp“.

Rufen Sie nach dem Einrichten des Listeners discoverServices() auf und übergeben Sie den Diensttyp das Ihre Anwendung suchen soll, das zu verwendende Discovery-Protokoll und den Sie gerade erstellt haben.

Kotlin

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

Java

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

Verbindung zu Diensten im Netzwerk herstellen

Wenn Ihre Anwendung im Netzwerk einen Dienst findet, zu dem eine Verbindung hergestellt werden kann, müssen zuerst die Verbindungsinformationen für diesen Dienst mithilfe der Methode resolveService()-Methode. Implementiere eine NsdManager.ResolveListener, die an dieses übergeben wird und verwenden Sie, um ein NsdServiceInfo-Element abzurufen, das die Verbindungsinformationen abrufen.

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

Sobald der Dienst gelöst ist, erhält Ihre Anwendung detaillierte Dienstinformationen, einschließlich IP-Adresse und Portnummer. Das ist alles müssen Sie eine eigene Netzwerkverbindung zum Dienst herstellen.

Registrierung des Dienstes beim Schließen der Anwendung aufheben

Es ist wichtig, NSD zu aktivieren und zu deaktivieren die während der Laufzeit der App Lebenszyklus. Wenn Sie die Registrierung Ihrer Anwendung aufheben, wenn sie beendet wird, andere Anwendungen nicht den Eindruck bekommen, dass sie noch aktiv sind, und versuchen, . Außerdem ist die Diensterkennung ein teurer Vorgang und sollte gestoppt werden wenn die übergeordnete Aktivität pausiert wird, und wieder aktiviert, wenn die Aktivität wurde(n) fortgesetzt. Lebenszyklusmethoden der Hauptaktivität überschreiben und Code einfügen um die Übertragung und Erkennung von Diensten zu starten und zu beenden.

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