NsdManager 发现两个服务,但是解析ip 和 port 的时候,只能解析一个,什么原因?

66 阅读2分钟

背景:

当你使用NsdManager发现多个服务并尝试解析IP和端口时,只能解析一个服务。

可能原因:

  1. 竞争条件:如果你尝试同时解析多个服务,可能会出现竞争条件,导致只有一个解析成功。
  2. 服务解析限制:NsdManager可能在某一时刻只允许解析一个服务。
  3. 回调覆盖:如果你使用了同一个ResolveListener实例解析多个服务,可能会导致回调被覆盖,从而只处理最后一个解析的服务。

尝试方法:

  1. 1. 确保每个服务使用单独的ResolveListener实例: 每个服务解析请求应该有自己独立的ResolveListener实例。

  2. 2. 逐一解析服务: 避免同时解析多个服务,而是一个接一个地解析,确保上一个解析完成后再开始下一个。

改动前的代码:

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Log;
import androidx.annotation.NonNull;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class NsdHelper {
private static final String TAG = "NsdHelper";
private NsdManager nsdManager;
private NsdManager.ResolveListener resolveListener;

public NsdHelper(Context context) {
nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
}

public void resolveService(@NonNull NsdServiceInfo serviceInfo) {
Executor executor = Executors.newSingleThreadExecutor();
resolveListener = new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "Resolve failed: " + errorCode);
}

@Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.i(TAG, "Service resolved: " + serviceInfo);
// Here you can get the resolved service details
String host = serviceInfo.getHost().getHostAddress();
int port = serviceInfo.getPort();
Log.i(TAG, "Host: " + host + ", Port: " + port);
}
};

nsdManager.resolveService(serviceInfo, executor, resolveListener);
}
}

改动后的代码:

以下是改进后的Kotlin代码示例,展示了如何处理多个服务的解析:

import android.content.Context
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.util.Log
import java.util.concurrent.Executors

class NsdHelper(context: Context) {

private val nsdManager: NsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
private val executor = Executors.newSingleThreadExecutor()
private val serviceQueue = mutableListOf()

fun discoverServices() {
val discoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(serviceType: String) {
Log.i(TAG, "Service discovery started")
}

override fun onServiceFound(serviceInfo: NsdServiceInfo) {
Log.i(TAG, "Service found: $serviceInfo")
// 将发现的服务添加到队列中等待解析
serviceQueue.add(serviceInfo)
if (serviceQueue.size == 1) {
resolveNextService()
}
}

override fun onServiceLost(serviceInfo: NsdServiceInfo) {
Log.e(TAG, "Service lost: $serviceInfo")
serviceQueue.remove(serviceInfo)
}

override fun onDiscoveryStopped(serviceType: String) {
Log.i(TAG, "Service discovery stopped")
}

override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
Log.e(TAG, "Service discovery start failed: $errorCode")
nsdManager.stopServiceDiscovery(this)
}

override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
Log.e(TAG, "Service discovery stop failed: $errorCode")
nsdManager.stopServiceDiscovery(this)
}
}

nsdManager.discoverServices("_http._tcp.", NsdManager.PROTOCOL_DNS_SD, discoveryListener)
}

private fun resolveNextService() {
if (serviceQueue.isEmpty()) return

val serviceInfo = serviceQueue.first()
val resolveListener = object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
Log.e(TAG, "Resolve failed: $errorCode")
serviceQueue.remove(serviceInfo)
resolveNextService()
}

override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
Log.i(TAG, "Service resolved: serviceInfo")valhost=serviceInfo.host.hostAddressvalport=serviceInfo.portLog.i(TAG,"Host:serviceInfo") val host = serviceInfo.host.hostAddress val port = serviceInfo.port Log.i(TAG, "Host: host, Port: $port")
serviceQueue.remove(serviceInfo)
resolveNextService()
}
}

nsdManager.resolveService(serviceInfo, executor, resolveListener)
}

companion object {
private const val TAG = "NsdHelper"
}
}

代码解释:

  1. 服务队列:使用一个MutableList来保存发现的服务。
  2. 发现服务:通过discoverServices方法启动服务发现。
  3. 解析服务:在onServiceFound回调中,将发现的服务添加到队列中,并调用resolveNextService方法开始解析。
  4. 逐一解析:resolveNextService方法从队列中取出第一个服务进行解析,并在解析完成或失败后解析下一个服务。

以上方式确保每次只解析一个服务,从而避免竞争条件和回调覆盖的问题。