Android NsdManager не может обнаружить службы

Я сталкиваюсь с проблемой с Androids NsdManager, когда следую их учебному пособию Использование обнаружения сетевых служб.

В моей сети есть несколько аппаратных устройств zeroconf/bonjour. Из моего Mac я могу найти все их, как и ожидалось, из моего терминала со следующим dns-sd -Z _my-mesh._tcp.

С первого запуска моего приложения для Android я могу безупречно обнаружить эти сервисы с помощью NsdManager. Однако, если я перезапущу приложение и попробую еще раз, ни одна из служб не будет найдена. onDiscoveryStarted вызывается успешно, но потом ничего больше. В ожидании я могу подтвердить с моего Mac, что службы все еще там успешно.

Затем я могу включить свое приложение Zeroconf (на Android), и оно покажет такие сервисы, как мой Mac. Когда я возвращаюсь к своему приложению, я вижу, что оно немедленно получает все обратные вызовы, которые я ожидал ранее. Поэтому я считаю, что с моим подходом что-то не так, однако я не уверен, что именно. Ниже приведен код, который я использую для обнаружения и разрешения сервисов. Представление - это гигантское представление текста (в виде прокрутки), для которого я продолжаю писать текст для упрощения отладки.

import android.annotation.SuppressLint
import android.content.Context
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

class MainActivity : AppCompatActivity(),
                     NsdManager.DiscoveryListener {

    private var nsdManager: NsdManager? = null
    private var text: TextView? = null
    private var isResolving = false
    private val services = ArrayList<ServiceWrapper>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        this.text = findViewById(R.id.text)
        this.nsdManager = application.getSystemService(Context.NSD_SERVICE) as NsdManager

    }

    override fun onResume() {
        super.onResume()
        this.nsdManager?.discoverServices("_my-mesh._tcp.", NsdManager.PROTOCOL_DNS_SD, this)
        write("Resume Discovering Services")
    }

    override fun onPause() {
        super.onPause()
        this.nsdManager?.stopServiceDiscovery(this)
        write("Pause Discovering Services")
    }

    override fun onServiceFound(serviceInfo: NsdServiceInfo?) {
        write("onServiceFound(serviceInfo = $serviceInfo))")
        if (serviceInfo == null) {
            return
        }
        add(serviceInfo)
    }

    override fun onStopDiscoveryFailed(serviceType: String?, errorCode: Int) {
        write("onStopDiscoveryFailed(serviceType = $serviceType, errorCode = $errorCode)")
    }

    override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) {
        write("onStartDiscoveryFailed(serviceType = $serviceType, errorCode = $errorCode)")
    }

    override fun onDiscoveryStarted(serviceType: String?) {
        write("onDiscoveryStarted(serviceType = $serviceType)")
    }

    override fun onDiscoveryStopped(serviceType: String?) {
        write("onDiscoveryStopped(serviceType = $serviceType)")
    }

    override fun onServiceLost(serviceInfo: NsdServiceInfo?) {
        write("onServiceLost(serviceInfo = $serviceInfo)")
    }

    private fun createResolveListener(): NsdManager.ResolveListener {
        return object : NsdManager.ResolveListener {
            override fun onResolveFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
                write("onResolveFailed(serviceInfo = $serviceInfo, errorCode = $errorCode)")
                isResolving = false
                resolveNext()
            }

            override fun onServiceResolved(serviceInfo: NsdServiceInfo?) {
                write("onServiceResolved(serviceInfo = $serviceInfo)")
                if (serviceInfo == null) {
                    return
                }
                for (servicewrapper in services) {
                    if (servicewrapper.serviceInfo.serviceName == serviceInfo.serviceName) {
                        servicewrapper.resolve(serviceInfo)
                    }
                }
                isResolving = false
                resolveNext()
            }
        }
    }

    @SuppressLint("SetTextI18n")
    private fun write(text: String?) {
        this.text?.let {
            it.post({
                        it.text = it.text.toString() + "\n" + text + "\n"
                    })
        }
    }

    fun add(serviceInfo: NsdServiceInfo) {
        for (servicewrapper in services) {
            if (servicewrapper.serviceInfo.serviceName == serviceInfo.serviceName) {
                return
            }
        }
        services.add(ServiceWrapper(serviceInfo))
        resolveNext()
    }

    @Synchronized
    fun resolveNext() {
        if (isResolving) {
            return
        }
        isResolving = true
        for (servicewrapper in services) {
            if (servicewrapper.isResolved) {
                continue
            }
            write("resolving")
            this.nsdManager?.resolveService(servicewrapper.serviceInfo, createResolveListener())
            return
        }
        isResolving = false

    }

    inner class ServiceWrapper(var serviceInfo: NsdServiceInfo) {

        var isResolved = false

        fun resolve(serviceInfo: NsdServiceInfo) {
            isResolved = true
            this.serviceInfo = serviceInfo


        }

    }


}

1 ответ

Лучше поздно, чем никогда. До сих пор не осознавал, что у других людей тоже была эта проблема.

Мы обнаружили, что некоторые маршрутизаторы блокируют или неправильно пересылают пакеты туда и обратно. Наше решение этой проблемы заключалось в использовании Wire Shark, чтобы определить, что делают другие популярные приложения, чтобы обойти проблему. Android NsdManager имеет ограниченные возможности настройки, поэтому требуется вручную передавать пакет через MulticastSocket.

      interface NsdDiscovery {

suspend fun startDiscovery()
suspend fun stopDiscovery()
fun setListener(listener: Listener?)
fun isDiscovering(): Boolean

interface Listener {

    fun onServiceFound(ip:String, local:String)
    fun onServiceLost(event: ServiceEvent)

}

}

@Singleton
class ManualNsdDiscovery @Inject constructor()
: NsdDiscovery {

//region Fields

private val isDiscovering = AtomicBoolean(false)

private var socketManager: SocketManager? = null

private var listener: WeakReference<NsdDiscovery.Listener> = WeakReference<NsdDiscovery.Listener>(null)

//endregion

//region NsdDiscovery

override suspend fun startDiscovery() = withContext(Dispatchers.IO) {
    if (isDiscovering()) return@withContext
    this@ManualNsdDiscovery.isDiscovering.set(true)
    val socketManager = SocketManager()
    socketManager.start()
    this@ManualNsdDiscovery.socketManager = socketManager

}

override suspend fun stopDiscovery() = withContext(Dispatchers.IO) {
    if (!isDiscovering()) return@withContext
    this@ManualNsdDiscovery.socketManager?.stop()
    this@ManualNsdDiscovery.socketManager = null
    this@ManualNsdDiscovery.isDiscovering.set(false)
}

override fun setListener(listener: NsdDiscovery.Listener?) {
    this.listener = WeakReference<NsdDiscovery.Listener>(listener)
}

@Synchronized
override fun isDiscovering(): Boolean {
    return this.isDiscovering.get()
}

//endregion

private inner class SocketManager {

    //region Fields

    private val group = InetAddress.getByName("224.0.0.251")
                        ?: throw IllegalStateException("Can't setup group")

    private val incomingNsd = IncomingNsd()

    private val outgoingNsd = OutgoingNsd()

    //endregion

    //region Constructors


    //endregion

    //region Methods

    suspend fun start() {
        this.incomingNsd.startListening()
        this.outgoingNsd.send()
    }

    fun stop() {
        this.incomingNsd.stopListening()
    }


    //endregion


    private inner class OutgoingNsd {

        //region Fields

        private val socketMutex = Mutex()

        private var socket = MulticastSocket(5353)

        suspend fun setUpSocket() {
            this.socketMutex.withLock {
                try {
                    this.socket = MulticastSocket(5353)
                    this.socket.reuseAddress = true
                    this.socket.joinGroup(group)
                } catch (e: SocketException) {
                    return
                }
            }
        }

        suspend fun tearDownSocket() {
            this.socketMutex.withLock {
                this@OutgoingNsd.socket.close()
            }
        }

        //ugly code but here is the packet
        private val bytes = byteArrayOf(171.toByte(), 205.toByte(), 1.toByte(), 32.toByte(),
                                        0.toByte(), 1.toByte(), 0.toByte(), 0.toByte(),
                                        0.toByte(), 0.toByte(), 0.toByte(), 0.toByte(),
                                        9.toByte(), 95.toByte(), 101.toByte(), 118.toByte(),
                                        97.toByte(), 45.toByte(), 109.toByte(), 101.toByte(),
                                        115.toByte(), 104.toByte(), 4.toByte(), 95.toByte(),
                                        116.toByte(), 99.toByte(), 112.toByte(), 5.toByte(),
                                        108.toByte(), 111.toByte(), 99.toByte(), 97.toByte(),
                                        108.toByte(), 0.toByte(), 0.toByte(), 12.toByte(),
                                        0.toByte(), 1.toByte())


        private val outPacket = DatagramPacket(bytes,
                                               bytes.size,
                                               this@SocketManager.group,
                                               5353)

        //endregion
        //region Methods

        @Synchronized
        suspend fun send() {
            withContext(Dispatchers.Default) {
                setUpSocket()
                try {
                    this@OutgoingNsd.socket.send(this@OutgoingNsd.outPacket)
                    delay(1500L)
                    tearDownSocket()
                } catch (e: Exception) {

                }

            }
        }

        //endregion

    }

    private inner class IncomingNsd {

        //region Fields


        private val isRunning = AtomicBoolean(false)

        private var socket = MulticastSocket(5353)

        //endregion

        //region Any

        fun setUpSocket() {
            try {
                this.socket = MulticastSocket(5353)
                this.socket.reuseAddress = true
                this.socket.joinGroup(group)
            } catch (e: SocketException) {

            } catch (e: BindException) {

            }
        }

        fun run() {
            GlobalScope.launch(Dispatchers.Default) {
                setUpSocket()
                try {
                    while (this@IncomingNsd.isRunning.get()) {
                        val bytes = ByteArray(4096)
                        val inPacket = DatagramPacket(bytes, bytes.size)
                        this@IncomingNsd.socket.receive(inPacket)
                        val incoming = DNSIncoming(inPacket)
                        for (answer in incoming.allAnswers) {
                            if (answer.key.contains("_my_mesh._tcp")) {
                                this@ManualNsdDiscovery.listener.get()?.onServiceFound(answer.recordSource.hostAddress, answer.name)
                                return@launch
                            }
                        }

                    }
                    this@IncomingNsd.socket.close()
                } catch (e: Exception) {

                }
            }
        }

        //endregion

        //region Methods

        @Synchronized
        fun startListening() {
            if (this.isRunning.get()) {
                return
            }
            this.isRunning.set(true)
            run()
        }

        @Synchronized
        fun stopListening() {
            if (!this.isRunning.get()) {
                return
            }
            this.isRunning.set(false)
        }

        //endregion

    }

}

}
Другие вопросы по тегам