Android、NSD/DNS-SD:NsdManager 不可靠的发现和 IP 解析
Android, NSD/DNS-SD: NsdManager unreliable discovery and IP resolution
在过去的几周里,Android 的 NSD 实施让我抓狂:
从用户的角度来看,会出现以下问题:
设备以完全不确定的方式相互发现。如果我启动基于 NsdManager
的应用程序,如果只涉及两个设备,它或多或少会起作用。 如果有第三个设备加入,它很少会发现前两个,前两个也不会发现第三个。如果我退出应用程序(它们优雅地取消注册 NSD 侦听器)并以不同的顺序重新启动它们,发现模式并不完全相同,但相似。
在我的家庭网络中,发现的设备的IP解析基本符合预期。在工作中,有时即使只使用两台设备(A 和 B),设备 A 也会使用 A 的 IP 地址和 B 的端口解析设备 B 的服务,反之亦然。因此,不知何故,IP 地址和服务名称似乎在较低级别(可能是 NsdManager)混淆了。
我现在已经就此 (https://code.google.com/p/android/issues/detail?id=201314&thanks=201314&ts=1455814995) 的 Google 代码提交了错误报告。我也在这里发布这个,希望得到更多的反馈;也许我的 Nsd 助手 class 有问题。
首先,经过无休止的调试,我现在在logcat中发现Android的底层NsdService
本身可能出现故障,而MDnsDS似乎可以正常工作.但我不确定...
这是一些说明问题的日志输出(为了便于阅读而过滤了一些消息):
02-18 16:57:02.327: D/NsdService(628): startMDnsDaemon
02-18 16:57:02.327: D/MDnsDS(187): Starting MDNSD
02-18 16:57:02.529: D/NsdService(628): New client listening to asynchronous messages
02-18 16:57:02.529: D/NsdService(628): New client, channel: com.android.internal.util.AsyncChannel@1fa188ce messenger: android.os.Messenger@cca33ef
02-18 16:57:02.532: D/NsdService(628): Register service
02-18 16:57:02.532: D/NsdService(628): registerService: 106 name: TuSync-0.57392, type: _tusync._tcp., host: /::, port: 57392
02-18 16:57:02.533: D/MDnsDS(187): serviceRegister(106, (null), TuSync-0.57392, _tusync._tcp., (null), (null), 57392, 0, <binary>)
02-18 16:57:02.533: D/MDnsDS(187): serviceRegister successful
02-18 16:57:02.534: D/NsdService(628): Register 1 106
02-18 16:57:04.083: D/MDnsDS(187): register succeeded for 106 as TuSync-0.57392
02-18 16:57:04.087: D/NsdService(628): SERVICE_REGISTERED Raw: 606 106 "TuSync-0.57392"
02-18 16:57:04.109: D/NsdService(628): Discover services
02-18 16:57:04.109: D/NsdService(628): discoverServices: 107 _tusync._tcp.
02-18 16:57:04.110: D/MDnsDS(187): discover((null), _tusync._tcp., (null), 107, 0)
02-18 16:57:04.110: D/MDnsDS(187): discover successful
02-18 16:57:04.110: D/NsdService(628): Discover 2 107_tusync._tcp.
02-18 16:57:04.333: D/MDnsDS(187): Discover found new serviceName TuSync-0.57392, regType _tusync._tcp. and domain local. for 107
02-18 16:57:04.334: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.57392" _tusync._tcp. local.
02-18 16:57:04.338: D/NsdService(628): Resolve service
02-18 16:57:04.338: D/NsdService(628): resolveService: 108 name: TuSync-0.57392, type: _tusync._tcp., host: null, port: 0
02-18 16:57:04.339: D/MDnsDS(187): resolveService(108, (null), TuSync-0.57392, _tusync._tcp., local.)
02-18 16:57:04.345: D/MDnsDS(187): startMonitoring 108
02-18 16:57:04.345: D/MDnsDS(187): resolveService successful
02-18 16:57:04.346: D/MDnsDS(187): resolve succeeded for 108 finding TuSync-0\.57392._tusync._tcp.local. at Android-3.local.:57392 with txtLen 1
02-18 16:57:04.347: D/NsdService(628): SERVICE_RESOLVED Raw: 608 108 "TuSync-0\.57392._tusync._tcp.local." "Android-3.local." 57392 1
02-18 16:57:04.347: D/NsdService(628): stopResolveService: 108
02-18 16:57:04.347: D/MDnsDS(187): Stopping resolve with ref 0xb5c4734c
02-18 16:57:04.349: D/NsdService(628): getAdddrInfo: 109
02-18 16:57:04.349: D/MDnsDS(187): getAddrInfo(109, (null) 0, Android-3.local.)
02-18 16:57:04.350: D/MDnsDS(187): getAddrInfo successful
02-18 16:57:04.352: D/MDnsDS(187): getAddrInfo succeeded for 109: 109 "Android-3.local." 120 10.0.0.4
02-18 16:57:04.352: D/MDnsDS(187): getAddrInfo succeeded for 109: 109 "Android-3.local." 120 fe80::204:4bff:fe2c:6c87
02-18 16:57:04.354: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 109 "Android-3.local." 120 10.0.0.4
02-18 16:57:04.354: D/NsdService(628): stopGetAdddrInfo: 109
02-18 16:57:04.355: D/MDnsDS(187): Stopping getaddrinfo with ref 0xb5c472d4
02-18 16:57:04.364: E/NsdService(628): Unique id with no client mapping: 109
02-18 16:57:04.364: E/NsdService(628): Unhandled { when=-10ms what=393242 obj=com.android.server.NsdService$NativeEvent@86af300 target=com.android.internal.util.StateMachine$SmHandler }
02-18 16:57:04.627: D/MDnsDS(187): Discover found new serviceName TuSync-0.36230, regType _tusync._tcp. and domain local. for 107
02-18 16:57:04.632: D/MDnsDS(187): Discover found new serviceName TuSync-0.60493, regType _tusync._tcp. and domain local. for 107
02-18 16:57:04.633: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.36230" _tusync._tcp. local.
02-18 16:57:04.634: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.60493" _tusync._tcp. local.
02-18 16:57:04.635: D/NsdService(628): Resolve service
02-18 16:57:04.635: D/NsdService(628): resolveService: 110 name: TuSync-0.36230, type: _tusync._tcp., host: null, port: 0
02-18 16:57:04.636: D/MDnsDS(187): resolveService(110, (null), TuSync-0.36230, _tusync._tcp., local.)
02-18 16:57:04.637: D/MDnsDS(187): resolve succeeded for 110 finding TuSync-0\.36230._tusync._tcp.local. at Android.local.:36230 with txtLen 1
02-18 16:57:04.638: D/NsdService(628): Resolve service
02-18 16:57:04.638: D/NsdService(628): SERVICE_RESOLVED Raw: 608 110 "TuSync-0\.36230._tusync._tcp.local." "Android.local." 36230 1
02-18 16:57:04.639: D/NsdService(628): stopResolveService: 110
02-18 16:57:04.639: D/MDnsDS(187): Stopping resolve with ref 0xb5c473c4
02-18 16:57:04.643: D/MDnsDS(187): getAddrInfo succeeded for 111: 111 "Android.local." 120 10.0.0.5
02-18 16:57:04.643: D/MDnsDS(187): getAddrInfo succeeded for 111: 111 "Android.local." 120 fe80::204:4bff:fe26:8483
02-18 16:57:04.644: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 111 "Android.local." 120 10.0.0.5
02-18 16:57:04.644: D/NsdService(628): stopGetAdddrInfo: 111
02-18 16:57:04.645: D/MDnsDS(187): Stopping getaddrinfo with ref 0xb5c47364
02-18 16:57:04.645: D/MDnsDS(187): Going to poll with pollCount 3
02-18 16:57:04.658: E/NsdService(628): Unique id with no client mapping: 111
02-18 16:57:04.658: E/NsdService(628): Unhandled { when=-14ms what=393242 obj=com.android.server.NsdService$NativeEvent@1d93a739 target=com.android.internal.util.StateMachine$SmHandler }
关于上下文的一些注释:
- 我的 NSD 服务类型是 _tusync._tcp.
- 我以 TuSync-0.[本地端口号] 格式为所有节点创建唯一的服务名称,以防止命名冲突并简化调试。
- 在这个测试场景中,有三个设备。日志设备的IP为10.0.0.4,端口57392.
日志显示,底层 MDnsDS
守护程序正确发现并解析了所有节点。但是,上面的 NsdService
不会传播所有这些的分辨率。 16:57:04.627 似乎存在 ID 冲突,设备的两个对等体(TuSync-0.36230 和 TuSync-0.60493)都分配了一个内部 ID 107(如果我通过查看日志)。我在 NsdManager
注册的 discoveryListener
会在发现两个节点时收到通知,但是,解析仅适用于其中一个节点,另一个会触发错误:
02-18 16:57:04.638: E/NsdHelper(6370): Resolve failed with error code:
3. Service: name: TuSync-0.60493, type: _tusync._tcp., host: null, port: 0
我还遇到过其他情况,在 NsdService
在日志中发出 "SERVICE_FOUND Raw" 消息后,我的发现侦听器没有收到通知。示例日志(经过严格过滤;与上述相同的测试设置):
02-18 17:54:06.692: D/MDnsDS(187): Starting MDNSD
02-18 17:54:06.896: D/NsdService(628): registerService: 112 name: TuSync-0.57392, type: _tusync._tcp., host: /::, port: 57392
02-18 17:54:06.896: D/MDnsDS(187): serviceRegister(112, (null), TuSync-0.57392, _tusync._tcp., (null), (null), 57392, 0, <binary>)
02-18 17:54:06.896: D/MDnsDS(187): serviceRegister successful
02-18 17:54:08.802: D/NsdService(628): SERVICE_REGISTERED Raw: 606 112 "TuSync-0.57392"
02-18 17:54:08.820: D/NsdService(628): Discover services
02-18 17:54:09.050: D/MDnsDS(187): Discover found new serviceName TuSync-0.57392, regType _tusync._tcp. and domain local. for 113
02-18 17:54:09.050: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.57392" _tusync._tcp. local.
02-18 17:54:09.211: D/MDnsDS(187): Discover found new serviceName TuSync-0.60493, regType _tusync._tcp. and domain local. for 113
02-18 17:54:09.212: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.60493" _tusync._tcp. local.
02-18 17:54:09.215: D/NsdService(628): resolveService: 116 name: TuSync-0.60493, type: _tusync._tcp., host: null, port: 0
02-18 17:54:09.216: D/MDnsDS(187): resolveService(116, (null), TuSync-0.60493, _tusync._tcp., local.)
02-18 17:54:09.217: D/MDnsDS(187): resolve succeeded for 116 finding TuSync-0\.60493._tusync._tcp.local. at Android-2.local.:60493 with txtLen 1
02-18 17:54:09.219: D/NsdService(628): SERVICE_RESOLVED Raw: 608 116 "TuSync-0\.60493._tusync._tcp.local." "Android-2.local." 60493 1
02-18 17:54:09.228: D/MDnsDS(187): getAddrInfo succeeded for 117: 117 "Android-2.local." 120 10.0.0.6
02-18 17:54:09.228: D/MDnsDS(187): getAddrInfo succeeded for 117: 117 "Android-2.local." 120 fe80::c643:8fff:fec5:5648
02-18 17:54:09.229: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 117 "Android-2.local." 120 10.0.0.6
02-18 17:54:09.244: D/MDnsDS(187): Discover found new serviceName TuSync-0.36230, regType _tusync._tcp. and domain local. for 113
02-18 17:54:09.251: E/NsdService(628): Unique id with no client mapping: 117
02-18 17:54:09.251: E/NsdService(628): Unhandled { when=-22ms what=393242 obj=com.android.server.NsdService$NativeEvent@1e992653 target=com.android.internal.util.StateMachine$SmHandler }
02-18 17:54:09.255: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.36230" _tusync._tcp. local.
在这种情况下,发现的对等点 10.0.0.5(端口 36230)不会触发 discoveryListener 通知。在最后一条日志消息之后,没有任何反应。所以我的日志记录节点 10.0.0.4 只发现了另一个节点 10.0.0.6:60493.
类似错误报告的数量很少让我想知道是否我是唯一遇到这些问题的人,或者 NsdManager 是否完全不稳定并且没有人使用它?
作为参考,这是我的助手 class 的代码 - 它类似于 Android NSD 聊天教程,但我试图改进它,因为该教程似乎存在其他一些错误挑衅。
public final class NsdHelper {
public static final String TAG = "NsdHelper";
private final Context mContext;
private final NsdManager mNsdManager;
private final String mBaseServiceName; // Base component of the service name, e.g. "service_xy"
private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, e.g. to "service_xy (2)"
private final String mServiceType;
private final NsdHandler mNsdHandler;
private MyRegistrationListener mRegistrationListener;
private final Object mRegistrationLock = new Object();
private MyDiscoveryListener mDiscoveryListener;
private final Object mDiscoveryLock = new Object();
private final Object mResolveLock = new Object();
private final Semaphore mResolveSemaphore;
public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) {
mContext = context;
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
mNsdHandler = nsdHandler;
mBaseServiceName = baseServiceName;
mServiceName = serviceName;
mServiceType = serviceType;
mResolveSemaphore = new Semaphore(10, true);
}
/*********************
* Lifecycle methods *
*********************/
public void registerLocalService(final int port) {
NsdServiceInfo localServiceInfo = new NsdServiceInfo();
localServiceInfo.setServiceName(mServiceName);
localServiceInfo.setServiceType(mServiceType);
localServiceInfo.setPort(port);
synchronized (mRegistrationLock) {
if (mRegistrationListener == null) {
mRegistrationListener = new MyRegistrationListener();
// try {
mNsdManager.registerService(
localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
/*} catch (Exception e) {
MLog.e(TAG, "Exception registering service; trying to unregister.", e);
unregisterLocalService();
mNsdHandler.onRegistrationFailed(localServiceInfo, 0);
}*/
} else {
MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered.");
}
}
}
public void unregisterLocalService() {
synchronized (mRegistrationLock) {
if (mRegistrationListener != null) {
// try {
mNsdManager.unregisterService(mRegistrationListener);
/*} catch (IllegalArgumentException e) {
MLog.w(TAG, "Exception trying to unregister registrationListener.");
}*/
mRegistrationListener = null;
} else {
MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered.");
}
}
}
public void startDiscovery() {
synchronized(mDiscoveryLock) {
if(mDiscoveryListener == null) {
mDiscoveryListener = new MyDiscoveryListener();
mNsdManager.discoverServices(
mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
} else {
MLog.w(TAG, "StartDiscovery called while discovery is already in progress.");
}
}
}
public void stopDiscovery() {
synchronized (mDiscoveryLock) {
if (mDiscoveryListener != null) {
mNsdManager.stopServiceDiscovery(mDiscoveryListener);
mDiscoveryListener = null;
} else {
MLog.w(TAG, "StopDiscovery called while no discovery is in progress.");
}
}
}
public void tearDown() {
MLog.v(TAG, "NsdHelper: tearDown()");
stopDiscovery();
unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered
}
/**
* Returns the current service name of the service.
* @return
*/
public String getServiceName() {
return mServiceName;
}
/**
* Convenience method to initiate service resolution
* @param serviceInfo NsdServiceInfo object for the service to be resolved
*/
private void resolveService(NsdServiceInfo serviceInfo) {
try {
MLog.vv(TAG, "Resolving service: acquiring semaphore.");
mResolveSemaphore.acquire();
MLog.vv(TAG, "Resolving service: semaphore acquired.");
} catch (InterruptedException e) {
MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted.");
}
mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName()));
}
/*************
* Listeners *
*************/
private class MyDiscoveryListener implements NsdManager.DiscoveryListener {
@Override
public void onDiscoveryStarted(String regType) {
MLog.d(TAG, "Service discovery started");
mNsdHandler.onDiscoveryStarted();
}
@Override
public void onServiceFound(NsdServiceInfo serviceInfo) {
MLog.d(TAG, "Discovered service: " + serviceInfo);
// Protocol matches?
if (!serviceInfo.getServiceType().equals(mServiceType)) {
MLog.v(TAG, "Discovered: other serviceType: " + serviceInfo.getServiceType());
}
// Make sure, that service name matches, and just resolve remote host
else if (serviceInfo.getServiceName().contains(mBaseServiceName)){
MLog.d(TAG, "Discovered: correct serviceType: " + mBaseServiceName);
resolveService(serviceInfo);
}
else {
// Other service name, log anyway
MLog.d(TAG, "Discovered: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring.");
}
}
@Override
public void onServiceLost(NsdServiceInfo service) {
MLog.e(TAG, "Service lost: " + service);
mNsdHandler.onRemotePeerLost(service);
}
@Override
public void onDiscoveryStopped(String serviceType) {
MLog.v(TAG, "Discovery stopped: " + serviceType);
mNsdHandler.onDiscoveryStopped();
}
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
MLog.e(TAG, "Discovery starting failed. Error code: " + errorCode);
synchronized (mDiscoveryLock) {
mDiscoveryListener = null; // just throw away the discovery listener, explicit stopping of the discovery should not be needed according to
// https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
}
}
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
MLog.e(TAG, "Discovery stopping failed. Error code: " + errorCode);
// try again
// mNsdManager.stopServiceDiscovery(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
}
};
private class MyRegistrationListener implements NsdManager.RegistrationListener {
@Override
public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) {
MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo);
boolean nameChanged = false;
// Update service name of this node (might change due to automatic conflict resolution!)
if(!mServiceName.equals(nsdServiceInfo.getServiceName())){
mServiceName = nsdServiceInfo.getServiceName();
nameChanged = true;
MLog.d(TAG, "Local service name updated to: " + mServiceName);
}
// Notify
if (mNsdHandler != null) {
mNsdHandler.onRegistrationSuccess(nsdServiceInfo);
if (nameChanged) {
mNsdHandler.onLocalServiceNameChanged(mServiceName);
}
} else {
MLog.w(TAG, "onServiceRegistered: NsdHandler is null.");
}
}
@Override
public void onRegistrationFailed(NsdServiceInfo arg0, int arg1) {
MLog.w(TAG, "Service registration failed with error code " + arg1 + ".");
if (mNsdHandler == null) {
MLog.w(TAG, "onRegistrationFailed: NsdHandler is null.");
return;
}
mNsdHandler.onRegistrationFailed(arg0, arg1);
}
@Override
public void onServiceUnregistered(NsdServiceInfo arg0) {
MLog.d(TAG, "Service unregistered.");
if (mNsdHandler == null) {
MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null.");
return;
}
}
@Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
MLog.w(TAG, "Service unregistering failed.");
if (mNsdHandler == null) {
MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null.");
return;
}
}
};
private class MyResolveListener implements NsdManager.ResolveListener {
private final String mServiceName;
public MyResolveListener(String serviceName) {
mServiceName = serviceName;
}
@Override
public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) {
// Release resource
mResolveSemaphore.release();
MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo);
if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) {
MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName());
}
}
@Override
public void onServiceResolved(final NsdServiceInfo serviceInfo) {
// Release resource
mResolveSemaphore.release();
MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort());
if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) {
MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName());
}
mNsdHandler.onNewRemotePeerResolved(serviceInfo);
}
};
/**
* Interface for handlers that deal just with essential NSD events.
* @author Alexander Fischl (alexander.fischl@semeion.net)
*/
public interface NsdHandler {
/**
* Called, when the NSD manager registered the service successfully.
* @param nsdServiceInfo
*/
public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the NSD registration was unsuccessful.
*/
public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode);
/**
* Called, when the NSD manager discovers a new peer. Services registered on the
* local machine DO NOT trigger this call!
* @param nsdServiceInfo
*/
public void onNewRemotePeerDiscovered(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the NSD manager resolves a new peer, yielding the connection data.
* Services registered on the local machine DO NOT trigger this call!
* @param nsdServiceInfo
*/
public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the NSD manager loses an already discovered peer.
* @param nsdServiceInfo
*/
public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the local service name needs to be updated (e.g. due to
* conflict resolution when the local service is registered, and the chosen service
* name is already taken by another node in the network.)
* @param newLocalServiceName
*/
public void onLocalServiceNameChanged(String newLocalServiceName);
/**
* Called, when the service discovery has successfully started.
*/
public void onDiscoveryStarted();
/**
* Called, when the service discovery was halted.
*/
public void onDiscoveryStopped();
}
}
请注意,我什至实现了一个可以设置为 1 的信号量,以防止并行解析多个服务,因为其他人报告了并行解析的问题。但是,将其设置为 1 不起作用,因为有时正在进行的解决方案既不会成功也不会失败;这会导致信号量不被释放并且 NsdManager 线程在下一个解析请求时永久卡住。
还有其他人遇到这样的问题吗? 如果成功使用 NsdManager 的人也发表评论,我会很高兴 - 这至少意味着我面临一个我可以解决的问题:)
我已经考虑放弃 NSD 并实施我自己的 broadcast/multicast 发现机制。这在理论上可能是一件轻而易举的事,但我读到 Android 上的多播也是一个 PITA,因为有些设备阻止它...
仍然与 Android NSD 没有区别。我一直在使用 Android 的 Marshmallow 版本,而 NSD 实际上仍然不可靠。我用 RxDNSSSD ( https://github.com/andriydruk/RxDNSSD ) 替换了 NSD。到目前为止,很少有线性代码可以完美运行。
我测试了 NSD 和 RXDNSSSD,当 RXDNSSSD 一直工作时,NSD 能够发现服务但无法解析 IP 地址。
希望对其他用户有所帮助。
它可能不再相关,但我发现了我的问题的可能原因之一:根据我的经验,如果您在 NsdManager 回调方法的线程中进行进一步处理,NSD 的行为似乎非常不可靠。即使只有几行代码,关于发现的最奇怪的结果也会发生。因此必须将任何工作从回调方法转移到另一个线程,并且不惜一切代价避免阻塞 NsdManager,即使是一毫秒。
我发现获取 MulticastLock improved that stability for me. See this answer.
在过去的几周里,Android 的 NSD 实施让我抓狂:
从用户的角度来看,会出现以下问题:
设备以完全不确定的方式相互发现。如果我启动基于
NsdManager
的应用程序,如果只涉及两个设备,它或多或少会起作用。 如果有第三个设备加入,它很少会发现前两个,前两个也不会发现第三个。如果我退出应用程序(它们优雅地取消注册 NSD 侦听器)并以不同的顺序重新启动它们,发现模式并不完全相同,但相似。在我的家庭网络中,发现的设备的IP解析基本符合预期。在工作中,有时即使只使用两台设备(A 和 B),设备 A 也会使用 A 的 IP 地址和 B 的端口解析设备 B 的服务,反之亦然。因此,不知何故,IP 地址和服务名称似乎在较低级别(可能是 NsdManager)混淆了。
我现在已经就此 (https://code.google.com/p/android/issues/detail?id=201314&thanks=201314&ts=1455814995) 的 Google 代码提交了错误报告。我也在这里发布这个,希望得到更多的反馈;也许我的 Nsd 助手 class 有问题。
首先,经过无休止的调试,我现在在logcat中发现Android的底层NsdService
本身可能出现故障,而MDnsDS似乎可以正常工作.但我不确定...
这是一些说明问题的日志输出(为了便于阅读而过滤了一些消息):
02-18 16:57:02.327: D/NsdService(628): startMDnsDaemon
02-18 16:57:02.327: D/MDnsDS(187): Starting MDNSD
02-18 16:57:02.529: D/NsdService(628): New client listening to asynchronous messages
02-18 16:57:02.529: D/NsdService(628): New client, channel: com.android.internal.util.AsyncChannel@1fa188ce messenger: android.os.Messenger@cca33ef
02-18 16:57:02.532: D/NsdService(628): Register service
02-18 16:57:02.532: D/NsdService(628): registerService: 106 name: TuSync-0.57392, type: _tusync._tcp., host: /::, port: 57392
02-18 16:57:02.533: D/MDnsDS(187): serviceRegister(106, (null), TuSync-0.57392, _tusync._tcp., (null), (null), 57392, 0, <binary>)
02-18 16:57:02.533: D/MDnsDS(187): serviceRegister successful
02-18 16:57:02.534: D/NsdService(628): Register 1 106
02-18 16:57:04.083: D/MDnsDS(187): register succeeded for 106 as TuSync-0.57392
02-18 16:57:04.087: D/NsdService(628): SERVICE_REGISTERED Raw: 606 106 "TuSync-0.57392"
02-18 16:57:04.109: D/NsdService(628): Discover services
02-18 16:57:04.109: D/NsdService(628): discoverServices: 107 _tusync._tcp.
02-18 16:57:04.110: D/MDnsDS(187): discover((null), _tusync._tcp., (null), 107, 0)
02-18 16:57:04.110: D/MDnsDS(187): discover successful
02-18 16:57:04.110: D/NsdService(628): Discover 2 107_tusync._tcp.
02-18 16:57:04.333: D/MDnsDS(187): Discover found new serviceName TuSync-0.57392, regType _tusync._tcp. and domain local. for 107
02-18 16:57:04.334: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.57392" _tusync._tcp. local.
02-18 16:57:04.338: D/NsdService(628): Resolve service
02-18 16:57:04.338: D/NsdService(628): resolveService: 108 name: TuSync-0.57392, type: _tusync._tcp., host: null, port: 0
02-18 16:57:04.339: D/MDnsDS(187): resolveService(108, (null), TuSync-0.57392, _tusync._tcp., local.)
02-18 16:57:04.345: D/MDnsDS(187): startMonitoring 108
02-18 16:57:04.345: D/MDnsDS(187): resolveService successful
02-18 16:57:04.346: D/MDnsDS(187): resolve succeeded for 108 finding TuSync-0\.57392._tusync._tcp.local. at Android-3.local.:57392 with txtLen 1
02-18 16:57:04.347: D/NsdService(628): SERVICE_RESOLVED Raw: 608 108 "TuSync-0\.57392._tusync._tcp.local." "Android-3.local." 57392 1
02-18 16:57:04.347: D/NsdService(628): stopResolveService: 108
02-18 16:57:04.347: D/MDnsDS(187): Stopping resolve with ref 0xb5c4734c
02-18 16:57:04.349: D/NsdService(628): getAdddrInfo: 109
02-18 16:57:04.349: D/MDnsDS(187): getAddrInfo(109, (null) 0, Android-3.local.)
02-18 16:57:04.350: D/MDnsDS(187): getAddrInfo successful
02-18 16:57:04.352: D/MDnsDS(187): getAddrInfo succeeded for 109: 109 "Android-3.local." 120 10.0.0.4
02-18 16:57:04.352: D/MDnsDS(187): getAddrInfo succeeded for 109: 109 "Android-3.local." 120 fe80::204:4bff:fe2c:6c87
02-18 16:57:04.354: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 109 "Android-3.local." 120 10.0.0.4
02-18 16:57:04.354: D/NsdService(628): stopGetAdddrInfo: 109
02-18 16:57:04.355: D/MDnsDS(187): Stopping getaddrinfo with ref 0xb5c472d4
02-18 16:57:04.364: E/NsdService(628): Unique id with no client mapping: 109
02-18 16:57:04.364: E/NsdService(628): Unhandled { when=-10ms what=393242 obj=com.android.server.NsdService$NativeEvent@86af300 target=com.android.internal.util.StateMachine$SmHandler }
02-18 16:57:04.627: D/MDnsDS(187): Discover found new serviceName TuSync-0.36230, regType _tusync._tcp. and domain local. for 107
02-18 16:57:04.632: D/MDnsDS(187): Discover found new serviceName TuSync-0.60493, regType _tusync._tcp. and domain local. for 107
02-18 16:57:04.633: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.36230" _tusync._tcp. local.
02-18 16:57:04.634: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.60493" _tusync._tcp. local.
02-18 16:57:04.635: D/NsdService(628): Resolve service
02-18 16:57:04.635: D/NsdService(628): resolveService: 110 name: TuSync-0.36230, type: _tusync._tcp., host: null, port: 0
02-18 16:57:04.636: D/MDnsDS(187): resolveService(110, (null), TuSync-0.36230, _tusync._tcp., local.)
02-18 16:57:04.637: D/MDnsDS(187): resolve succeeded for 110 finding TuSync-0\.36230._tusync._tcp.local. at Android.local.:36230 with txtLen 1
02-18 16:57:04.638: D/NsdService(628): Resolve service
02-18 16:57:04.638: D/NsdService(628): SERVICE_RESOLVED Raw: 608 110 "TuSync-0\.36230._tusync._tcp.local." "Android.local." 36230 1
02-18 16:57:04.639: D/NsdService(628): stopResolveService: 110
02-18 16:57:04.639: D/MDnsDS(187): Stopping resolve with ref 0xb5c473c4
02-18 16:57:04.643: D/MDnsDS(187): getAddrInfo succeeded for 111: 111 "Android.local." 120 10.0.0.5
02-18 16:57:04.643: D/MDnsDS(187): getAddrInfo succeeded for 111: 111 "Android.local." 120 fe80::204:4bff:fe26:8483
02-18 16:57:04.644: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 111 "Android.local." 120 10.0.0.5
02-18 16:57:04.644: D/NsdService(628): stopGetAdddrInfo: 111
02-18 16:57:04.645: D/MDnsDS(187): Stopping getaddrinfo with ref 0xb5c47364
02-18 16:57:04.645: D/MDnsDS(187): Going to poll with pollCount 3
02-18 16:57:04.658: E/NsdService(628): Unique id with no client mapping: 111
02-18 16:57:04.658: E/NsdService(628): Unhandled { when=-14ms what=393242 obj=com.android.server.NsdService$NativeEvent@1d93a739 target=com.android.internal.util.StateMachine$SmHandler }
关于上下文的一些注释:
- 我的 NSD 服务类型是 _tusync._tcp.
- 我以 TuSync-0.[本地端口号] 格式为所有节点创建唯一的服务名称,以防止命名冲突并简化调试。
- 在这个测试场景中,有三个设备。日志设备的IP为10.0.0.4,端口57392.
日志显示,底层 MDnsDS
守护程序正确发现并解析了所有节点。但是,上面的 NsdService
不会传播所有这些的分辨率。 16:57:04.627 似乎存在 ID 冲突,设备的两个对等体(TuSync-0.36230 和 TuSync-0.60493)都分配了一个内部 ID 107(如果我通过查看日志)。我在 NsdManager
注册的 discoveryListener
会在发现两个节点时收到通知,但是,解析仅适用于其中一个节点,另一个会触发错误:
02-18 16:57:04.638: E/NsdHelper(6370): Resolve failed with error code:
3. Service: name: TuSync-0.60493, type: _tusync._tcp., host: null, port: 0
我还遇到过其他情况,在 NsdService
在日志中发出 "SERVICE_FOUND Raw" 消息后,我的发现侦听器没有收到通知。示例日志(经过严格过滤;与上述相同的测试设置):
02-18 17:54:06.692: D/MDnsDS(187): Starting MDNSD
02-18 17:54:06.896: D/NsdService(628): registerService: 112 name: TuSync-0.57392, type: _tusync._tcp., host: /::, port: 57392
02-18 17:54:06.896: D/MDnsDS(187): serviceRegister(112, (null), TuSync-0.57392, _tusync._tcp., (null), (null), 57392, 0, <binary>)
02-18 17:54:06.896: D/MDnsDS(187): serviceRegister successful
02-18 17:54:08.802: D/NsdService(628): SERVICE_REGISTERED Raw: 606 112 "TuSync-0.57392"
02-18 17:54:08.820: D/NsdService(628): Discover services
02-18 17:54:09.050: D/MDnsDS(187): Discover found new serviceName TuSync-0.57392, regType _tusync._tcp. and domain local. for 113
02-18 17:54:09.050: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.57392" _tusync._tcp. local.
02-18 17:54:09.211: D/MDnsDS(187): Discover found new serviceName TuSync-0.60493, regType _tusync._tcp. and domain local. for 113
02-18 17:54:09.212: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.60493" _tusync._tcp. local.
02-18 17:54:09.215: D/NsdService(628): resolveService: 116 name: TuSync-0.60493, type: _tusync._tcp., host: null, port: 0
02-18 17:54:09.216: D/MDnsDS(187): resolveService(116, (null), TuSync-0.60493, _tusync._tcp., local.)
02-18 17:54:09.217: D/MDnsDS(187): resolve succeeded for 116 finding TuSync-0\.60493._tusync._tcp.local. at Android-2.local.:60493 with txtLen 1
02-18 17:54:09.219: D/NsdService(628): SERVICE_RESOLVED Raw: 608 116 "TuSync-0\.60493._tusync._tcp.local." "Android-2.local." 60493 1
02-18 17:54:09.228: D/MDnsDS(187): getAddrInfo succeeded for 117: 117 "Android-2.local." 120 10.0.0.6
02-18 17:54:09.228: D/MDnsDS(187): getAddrInfo succeeded for 117: 117 "Android-2.local." 120 fe80::c643:8fff:fec5:5648
02-18 17:54:09.229: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 117 "Android-2.local." 120 10.0.0.6
02-18 17:54:09.244: D/MDnsDS(187): Discover found new serviceName TuSync-0.36230, regType _tusync._tcp. and domain local. for 113
02-18 17:54:09.251: E/NsdService(628): Unique id with no client mapping: 117
02-18 17:54:09.251: E/NsdService(628): Unhandled { when=-22ms what=393242 obj=com.android.server.NsdService$NativeEvent@1e992653 target=com.android.internal.util.StateMachine$SmHandler }
02-18 17:54:09.255: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.36230" _tusync._tcp. local.
在这种情况下,发现的对等点 10.0.0.5(端口 36230)不会触发 discoveryListener 通知。在最后一条日志消息之后,没有任何反应。所以我的日志记录节点 10.0.0.4 只发现了另一个节点 10.0.0.6:60493.
类似错误报告的数量很少让我想知道是否我是唯一遇到这些问题的人,或者 NsdManager 是否完全不稳定并且没有人使用它?
作为参考,这是我的助手 class 的代码 - 它类似于 Android NSD 聊天教程,但我试图改进它,因为该教程似乎存在其他一些错误挑衅。
public final class NsdHelper {
public static final String TAG = "NsdHelper";
private final Context mContext;
private final NsdManager mNsdManager;
private final String mBaseServiceName; // Base component of the service name, e.g. "service_xy"
private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, e.g. to "service_xy (2)"
private final String mServiceType;
private final NsdHandler mNsdHandler;
private MyRegistrationListener mRegistrationListener;
private final Object mRegistrationLock = new Object();
private MyDiscoveryListener mDiscoveryListener;
private final Object mDiscoveryLock = new Object();
private final Object mResolveLock = new Object();
private final Semaphore mResolveSemaphore;
public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) {
mContext = context;
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
mNsdHandler = nsdHandler;
mBaseServiceName = baseServiceName;
mServiceName = serviceName;
mServiceType = serviceType;
mResolveSemaphore = new Semaphore(10, true);
}
/*********************
* Lifecycle methods *
*********************/
public void registerLocalService(final int port) {
NsdServiceInfo localServiceInfo = new NsdServiceInfo();
localServiceInfo.setServiceName(mServiceName);
localServiceInfo.setServiceType(mServiceType);
localServiceInfo.setPort(port);
synchronized (mRegistrationLock) {
if (mRegistrationListener == null) {
mRegistrationListener = new MyRegistrationListener();
// try {
mNsdManager.registerService(
localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
/*} catch (Exception e) {
MLog.e(TAG, "Exception registering service; trying to unregister.", e);
unregisterLocalService();
mNsdHandler.onRegistrationFailed(localServiceInfo, 0);
}*/
} else {
MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered.");
}
}
}
public void unregisterLocalService() {
synchronized (mRegistrationLock) {
if (mRegistrationListener != null) {
// try {
mNsdManager.unregisterService(mRegistrationListener);
/*} catch (IllegalArgumentException e) {
MLog.w(TAG, "Exception trying to unregister registrationListener.");
}*/
mRegistrationListener = null;
} else {
MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered.");
}
}
}
public void startDiscovery() {
synchronized(mDiscoveryLock) {
if(mDiscoveryListener == null) {
mDiscoveryListener = new MyDiscoveryListener();
mNsdManager.discoverServices(
mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
} else {
MLog.w(TAG, "StartDiscovery called while discovery is already in progress.");
}
}
}
public void stopDiscovery() {
synchronized (mDiscoveryLock) {
if (mDiscoveryListener != null) {
mNsdManager.stopServiceDiscovery(mDiscoveryListener);
mDiscoveryListener = null;
} else {
MLog.w(TAG, "StopDiscovery called while no discovery is in progress.");
}
}
}
public void tearDown() {
MLog.v(TAG, "NsdHelper: tearDown()");
stopDiscovery();
unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered
}
/**
* Returns the current service name of the service.
* @return
*/
public String getServiceName() {
return mServiceName;
}
/**
* Convenience method to initiate service resolution
* @param serviceInfo NsdServiceInfo object for the service to be resolved
*/
private void resolveService(NsdServiceInfo serviceInfo) {
try {
MLog.vv(TAG, "Resolving service: acquiring semaphore.");
mResolveSemaphore.acquire();
MLog.vv(TAG, "Resolving service: semaphore acquired.");
} catch (InterruptedException e) {
MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted.");
}
mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName()));
}
/*************
* Listeners *
*************/
private class MyDiscoveryListener implements NsdManager.DiscoveryListener {
@Override
public void onDiscoveryStarted(String regType) {
MLog.d(TAG, "Service discovery started");
mNsdHandler.onDiscoveryStarted();
}
@Override
public void onServiceFound(NsdServiceInfo serviceInfo) {
MLog.d(TAG, "Discovered service: " + serviceInfo);
// Protocol matches?
if (!serviceInfo.getServiceType().equals(mServiceType)) {
MLog.v(TAG, "Discovered: other serviceType: " + serviceInfo.getServiceType());
}
// Make sure, that service name matches, and just resolve remote host
else if (serviceInfo.getServiceName().contains(mBaseServiceName)){
MLog.d(TAG, "Discovered: correct serviceType: " + mBaseServiceName);
resolveService(serviceInfo);
}
else {
// Other service name, log anyway
MLog.d(TAG, "Discovered: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring.");
}
}
@Override
public void onServiceLost(NsdServiceInfo service) {
MLog.e(TAG, "Service lost: " + service);
mNsdHandler.onRemotePeerLost(service);
}
@Override
public void onDiscoveryStopped(String serviceType) {
MLog.v(TAG, "Discovery stopped: " + serviceType);
mNsdHandler.onDiscoveryStopped();
}
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
MLog.e(TAG, "Discovery starting failed. Error code: " + errorCode);
synchronized (mDiscoveryLock) {
mDiscoveryListener = null; // just throw away the discovery listener, explicit stopping of the discovery should not be needed according to
// https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
}
}
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
MLog.e(TAG, "Discovery stopping failed. Error code: " + errorCode);
// try again
// mNsdManager.stopServiceDiscovery(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
}
};
private class MyRegistrationListener implements NsdManager.RegistrationListener {
@Override
public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) {
MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo);
boolean nameChanged = false;
// Update service name of this node (might change due to automatic conflict resolution!)
if(!mServiceName.equals(nsdServiceInfo.getServiceName())){
mServiceName = nsdServiceInfo.getServiceName();
nameChanged = true;
MLog.d(TAG, "Local service name updated to: " + mServiceName);
}
// Notify
if (mNsdHandler != null) {
mNsdHandler.onRegistrationSuccess(nsdServiceInfo);
if (nameChanged) {
mNsdHandler.onLocalServiceNameChanged(mServiceName);
}
} else {
MLog.w(TAG, "onServiceRegistered: NsdHandler is null.");
}
}
@Override
public void onRegistrationFailed(NsdServiceInfo arg0, int arg1) {
MLog.w(TAG, "Service registration failed with error code " + arg1 + ".");
if (mNsdHandler == null) {
MLog.w(TAG, "onRegistrationFailed: NsdHandler is null.");
return;
}
mNsdHandler.onRegistrationFailed(arg0, arg1);
}
@Override
public void onServiceUnregistered(NsdServiceInfo arg0) {
MLog.d(TAG, "Service unregistered.");
if (mNsdHandler == null) {
MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null.");
return;
}
}
@Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
MLog.w(TAG, "Service unregistering failed.");
if (mNsdHandler == null) {
MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null.");
return;
}
}
};
private class MyResolveListener implements NsdManager.ResolveListener {
private final String mServiceName;
public MyResolveListener(String serviceName) {
mServiceName = serviceName;
}
@Override
public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) {
// Release resource
mResolveSemaphore.release();
MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo);
if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) {
MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName());
}
}
@Override
public void onServiceResolved(final NsdServiceInfo serviceInfo) {
// Release resource
mResolveSemaphore.release();
MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort());
if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) {
MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName());
}
mNsdHandler.onNewRemotePeerResolved(serviceInfo);
}
};
/**
* Interface for handlers that deal just with essential NSD events.
* @author Alexander Fischl (alexander.fischl@semeion.net)
*/
public interface NsdHandler {
/**
* Called, when the NSD manager registered the service successfully.
* @param nsdServiceInfo
*/
public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the NSD registration was unsuccessful.
*/
public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode);
/**
* Called, when the NSD manager discovers a new peer. Services registered on the
* local machine DO NOT trigger this call!
* @param nsdServiceInfo
*/
public void onNewRemotePeerDiscovered(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the NSD manager resolves a new peer, yielding the connection data.
* Services registered on the local machine DO NOT trigger this call!
* @param nsdServiceInfo
*/
public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the NSD manager loses an already discovered peer.
* @param nsdServiceInfo
*/
public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo);
/**
* Called, when the local service name needs to be updated (e.g. due to
* conflict resolution when the local service is registered, and the chosen service
* name is already taken by another node in the network.)
* @param newLocalServiceName
*/
public void onLocalServiceNameChanged(String newLocalServiceName);
/**
* Called, when the service discovery has successfully started.
*/
public void onDiscoveryStarted();
/**
* Called, when the service discovery was halted.
*/
public void onDiscoveryStopped();
}
}
请注意,我什至实现了一个可以设置为 1 的信号量,以防止并行解析多个服务,因为其他人报告了并行解析的问题。但是,将其设置为 1 不起作用,因为有时正在进行的解决方案既不会成功也不会失败;这会导致信号量不被释放并且 NsdManager 线程在下一个解析请求时永久卡住。
还有其他人遇到这样的问题吗? 如果成功使用 NsdManager 的人也发表评论,我会很高兴 - 这至少意味着我面临一个我可以解决的问题:)
我已经考虑放弃 NSD 并实施我自己的 broadcast/multicast 发现机制。这在理论上可能是一件轻而易举的事,但我读到 Android 上的多播也是一个 PITA,因为有些设备阻止它...
仍然与 Android NSD 没有区别。我一直在使用 Android 的 Marshmallow 版本,而 NSD 实际上仍然不可靠。我用 RxDNSSSD ( https://github.com/andriydruk/RxDNSSD ) 替换了 NSD。到目前为止,很少有线性代码可以完美运行。
我测试了 NSD 和 RXDNSSSD,当 RXDNSSSD 一直工作时,NSD 能够发现服务但无法解析 IP 地址。
希望对其他用户有所帮助。
它可能不再相关,但我发现了我的问题的可能原因之一:根据我的经验,如果您在 NsdManager 回调方法的线程中进行进一步处理,NSD 的行为似乎非常不可靠。即使只有几行代码,关于发现的最奇怪的结果也会发生。因此必须将任何工作从回调方法转移到另一个线程,并且不惜一切代价避免阻塞 NsdManager,即使是一毫秒。
我发现获取 MulticastLock improved that stability for me. See this answer.