1

I want to connect multiple Android devices via Network Service Discovery. I want to use a star topology. so on device should discover and the others a registering a service which should found by the discoverer. For this I took the NSD Chat (https://github.com/gauravcanon/NsdChat ) NsdHelper and changed it only a bit, so a callback gets called after resolving a service which establish a connection. This works in mostly if I am using three devices. two advertiser and one discoverer. The services getting resolved and a connection estabished. If I get an additional third advertiser it is crashing in 80% of all attempts. The reason is that the serviceInfo which is passed in the onServiceResolved function in the ResolveListener contains the ip address of the resolving phone and not of the advertising one. The port is correct and the servicename also. That is such a strange behaviour, I don't know how to debug this. I'm using the bonjour browser to see all registered services and the registration of all services is fine. All service infos containing the right ip address and port. I also tried the the pure discovering process without establishing a connection. Same failure. Sometimes the ip address of the resolver is in the serviceinfo. It can also happen at the first discovery and the second, but most likely it is on the third one. I will post my NsdHelper code below. I made some edits right now because i tried to start the discovery process again after resolving, so there are more differences to the NSD Chat, but the error persists. Is someone using the NSD implementation of Android via the NSDManager with multiple devices? Is it working for you? What are you doing different? This topic is related there someone had the same problems ( Android, NSD/DNS-SD: NsdManager unreliable discovery and IP resolution ). I cant imagine that this error is still a thing 3 years ago?

I'm thankful for every hint!

    class NsdHelper(private var mContext: Context, private val createClient: (InetAddress, Int)->Unit, val mHandler: Handler) {
internal var mNsdManager: NsdManager = mContext.getSystemService(Context.NSD_SERVICE) as NsdManager
internal lateinit var mResolveListener: NsdManager.ResolveListener
private var mDiscoveryListener: NsdManager.DiscoveryListener? = null
private var mRegistrationListener: NsdManager.RegistrationListener? = null
var mServiceName = "Wizard"
var chosenServiceInfo: NsdServiceInfo? = null
    internal set
val mServices = mutableListOf<NsdServiceInfo>()

fun initializeNsd() {
    stopDiscovery()
    tearDown()
    initializeResolveListener()
}


fun reset(){
    initializeResolveListener()
    discoverServices()
}

fun initializeDiscoveryListener() {
    mDiscoveryListener = object : NsdManager.DiscoveryListener {
        override fun onDiscoveryStarted(regType: String) {
            Log.d(TAG, "Service discovery started")
        }

        override fun onServiceFound(service: NsdServiceInfo) {
            Log.d(TAG, "Service discovery success$service")
            when {
                service.serviceType != SERVICE_TYPE -> Log.d(TAG, "Unknown Service Type: " + service.serviceType)
                service.serviceName == mServiceName -> Log.d(TAG, "Same machine: $mServiceName")
                service.serviceName.contains("Verzauberte") -> {
                    if (mServices.none { it.serviceName == service.serviceName })
                    mNsdManager.resolveService(service, mResolveListener)
                }
            }
        }

        override fun onServiceLost(service: NsdServiceInfo) {
            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")
        }

        override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
            Log.e(TAG, "Discovery failed: Error code:$errorCode")
        }
    }
}

fun initializeResolveListener() {
    mResolveListener = object : NsdManager.ResolveListener {
        override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
            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
            }
            chosenServiceInfo = serviceInfo
            mHandler.post(Runnable {
                createClient(
                    serviceInfo.host,
                    serviceInfo.port
                )
            })
            mServices.add(serviceInfo)
            reset()
        }
    }
}

fun initializeRegistrationListener() {
    mRegistrationListener = object : NsdManager.RegistrationListener {
        override fun onServiceRegistered(NsdServiceInfo: NsdServiceInfo) {
            mServiceName = NsdServiceInfo.serviceName
            Log.d(TAG, "Service registered: $mServiceName")
        }

        override fun onRegistrationFailed(arg0: NsdServiceInfo, arg1: Int) {
            Log.d(TAG, "Service registration failed: $arg1")
        }

        override fun onServiceUnregistered(arg0: NsdServiceInfo) {
            Log.d(TAG, "Service unregistered: " + arg0.serviceName)
        }

        override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
            Log.d(TAG, "Service unregistration failed: $errorCode")
        }
    }
}

fun registerService(port: Int) {
    tearDown()  // Cancel any previous registration request
    initializeRegistrationListener()
    val serviceInfo = NsdServiceInfo().apply {
        serviceType = SERVICE_TYPE
        serviceName = "Verzauberte[$port]"
        setPort(port)
    }
    mNsdManager.registerService(
        serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener
    )
}

fun discoverServices() {
    stopDiscovery()  // Cancel any existing discovery request
    initializeDiscoveryListener()
    Log.d(this.toString(), "Start discovering")
    mNsdManager.discoverServices(
        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener
    )
}

fun stopDiscovery() {
    if (mDiscoveryListener != null) {
        try {
            mNsdManager.stopServiceDiscovery(mDiscoveryListener)
        } finally {
        }
        mDiscoveryListener = null
    }
}

fun tearDown() {
    if (mRegistrationListener != null) {
        try {
            mNsdManager.unregisterService(mRegistrationListener)
        } finally {
        }
        mRegistrationListener = null
    }
}



companion object {
    val SERVICE_TYPE = "_votinginteractions._tcp."
    val TAG = "NsdHelper"
}

}

60pfennig
  • 43
  • 6
  • you use the API in a wrong way. There should be only one advertiser and multiple discoverers. – Onik Mar 21 '19 at 07:14
  • Thank you for the hint! I'm a lucky man that I'm using a star topology so I could change it. But the Problem would persist if I would use a net topology. I experienced problems with my pixel while the discovery process beacuse i didn't aquire a multicast lock. Maybe there is a link between this topics?! – 60pfennig Mar 21 '19 at 13:16
  • 1
    A short update: The problem persists even after rewriting the application. Now There is only one service, which gets discovered from the other devices. The success of the discovery strongly depends on the type of network. At my home wifi it worked well. Also if all phones are connected with a wifi hotspot created with one phone. But in university (in a local network where multicast is allowed) the discoverer still resolving wrong ips. This time they even resolving ips from the other phone which are just discovering and didn't register a service. – 60pfennig Mar 22 '19 at 12:35

0 Answers0