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"
}
}