Android、NSD/DNS-SD:NsdManager 不可靠的发现和 IP 解析

2024-01-04

在过去的几周里,Android 的 NSD 实现让我抓狂:

从用户的角度来看,会出现以下问题:

  • 设备以完全不确定的方式发现彼此。如果我启动我的NsdManager基于应用程序,如果只涉及两个设备,它或多或少可以工作。如果第三个设备加入,它很少会发现前两个,前两个不会看到第三个。如果我退出应用程序(它们优雅地取消注册 NSD 侦听器)并以不同的顺序重新启动它们,则发现模式不完全相同,但相似。

  • 在我的家庭网络中,发现的设备的IP解析基本上按预期工作。在工作中,有时即使只使用两个设备(A 和 B),设备A将通过A的IP地址和B的端口解析设备B的服务反之亦然。因此,不知何故,IP 地址和服务名称似乎在较低级别(可能是 NsdManager)混淆了。

我现在已经为此提交了关于 Google 代码的错误报告()。我也将其发布在这里,希望得到更多反馈;也许我的 Nsd 帮助类中的某些内容是错误的。

首先,经过无休止的调试,我现在在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(如果我仅通过查看日志正确解释机制) 。这discoveryListener我注册了NsdManager在发现两个节点时收到通知,但是,解析仅适用于其中一个,另一个会触发错误:

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 是否完全不稳定并且没有人使用它?

作为参考,这里是我的助手类的代码 - 它类似于 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 ([email protected] /cdn-cgi/l/email-protection)
     */
    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 并实现我自己的广播/多播发现机制。理论上这可能是一件轻而易举的事,但我读到 Android 上的多播也是一个 PITA,因为某些设备阻止了它......


和Android NSD还是没有什么区别。我一直在使用 Marshmallow 版本的 Android,NSD 实际上仍然不可靠。我用 RxDNSSD 替换了 NSD(https://github.com/andriydruk/RxDNSSD https://github.com/andriydruk/RxDNSSD)。到目前为止,很少有班轮代码能够完美运行。

我测试了 NSD 和 RXDNSSD,NSD 能够发现服务,但无法解析 IP 地址,而 RXDNSSD 一直在工作。

希望它对其他用户有所帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android、NSD/DNS-SD:NsdManager 不可靠的发现和 IP 解析 的相关文章

  • JKS、BKS 和 PKCS12 文件格式

    我正在设置一个无头服务器 该服务器使用用户提供的数据 JS CSS HTML 密钥库 为 Android 构建 Phonegap 混合应用程序 我想进行一些基本的客户端检查 以确保上传的密钥库有效 对于 JKS 文件 我发现可以通过确保提供
  • GCM 通知的自定义 UI

    In GCM Docs http developer android com google gcm gcm html其给定 它不提供任何内置用户界面或其他处理 消息数据 GCM 只是将收到的原始消息数据直接传递给 Android 应用程序
  • 不支持动态值作为注释中的属性 - AspectJ Android [AOP Android]

    我正在使用自定义注释来记录用户单击的 id 但我收到一个错误 属性值必须是常量 我的代码片段如下 mAssetId Asset getContentId TrackEvent track event ArrayParams Params k
  • 应用程序启动时立即隐藏导航栏

    基于以下代码片段 我能够隐藏状态栏当应用程序启动时 但不是导航栏 由后退 主页和任务管理器按钮组成的栏 因为它隐藏了稍后在 MainActivity 的线程完成加载后 这是清单
  • API 31 上是否有官方方法来提供文件关联,可能使用 pathSuffix/pathAdvancedPattern?

    背景 现代桌面操作系统上的一个众所周知的功能是能够处理文件 允许用户从文件管理器和其他应用程序中打开它们 作为 文件关联 配置 问题 到目前为止 对于用户和开发人员来说 在 Android 上设置文件类型关联并不是一件很方便的事情 在 An
  • 将文本视图包裹在图像视图周围

    晚上好 我正在尝试将文本包裹在图像视图中 就像这里提出的问题一样 Textview 环绕 View https stackoverflow com questions 3626750 textview wrap around view 在风
  • 使用反向无限滚动添加到 ListView 时保持滚动位置

    我正在构建一个类似聊天的 Android 应用程序 类似于环聊 为此 我使用垂直 ListViewstackFromBottom true and transcriptMode normal 该列表按从较旧的消息 顶部 到较新的消息 底部
  • 在 doInBackground 方法中启动活动

    在下面的代码中 我从互联网下载 json 并希望显示在列表中 如果列表为空 则转到另一个活动 但其他活动未启动 没有错误 但没有启动活动 感谢您的帮助 package ir mohammadi android nightly import
  • 使用 appcelerator titan 在 android 中后退按钮退出应用程序

    我是钛开发的新手 在本机 android 中 如果我们按下后退按钮 则仅当前活动将被关闭 并且它将返回到上一个活动 但是当我使用 Titanium 在 Android 中按下后退按钮时 它会从应用程序退出 我怎样才能改变这种行为 有两种类型
  • 创建用于 Android 库分发的 JAR

    我正在开发一个 android 库 并希望导出一个 JAR 文件 我可以分发该文件供其他人在他们的应用程序中使用 我不想分发源代码 因为它包含有关发布到我的网络服务器的详细信息 我尝试使用在 bin 目录中创建的 JAR 文件并将该 jar
  • GCM(Google 云消息传递)是否需要 Google 帐户?

    我需要编写一个简单的应用程序来获取推送通知 我使用 GCM 它使用 Google Play 服务来获取信息 我的问题是 要访问 GCM 是否需要 Google 帐户 我可以使用其他电子邮件帐户来识别设备吗 有没有其他方法可以获取另一个电子邮
  • 在 android studio 上单击推送通知后重定向到特定活动

    我正在努力开发一个 Android 应用程序 以便在单击所有传入的推送通知时将应用程序重定向到特定的活动页面 我是 Android 开发新手 对我的编程感到抱歉 下面是推送通知部分的 android manifest xml 如果对此主题有
  • 如何使用 iPhone 将照片上传到服务器?

    我正在编写一个 iPhone 应用程序 它可以拍摄照片然后将其上传到服务器 如何使用 Cocoa 将照片上传到服务器 我想我在某处使用 NSUrl Thanks Header interface EPUploader NSObject NS
  • Android - 具有可序列化对象的 SharedPreferences

    我知道 SharedPreferences 有putString putFloat putLong putInt and putBoolean 但我需要存储一个类型的对象Serializable in SharedPreferences 我
  • 当创建 Android Jetpack Compose AndroidView 的参数发生变化时,如何替换它?

    我有一个应用程序 显示封装在其中的几个不同视图AndroidView 在下面重现的简单示例中 这些只是TextView实例 问题是更改文本 在本例中循环显示三个不同的值 似乎不会更新应用程序显示的内容 sealed class AppVie
  • 如何为工具栏上的溢出菜单中的菜单项设置字体

    我想更改项目的默认字体溢出菜单并设置自定义字体 我尝试添加一个工厂LayoutInflater并在onCreateView 方法我改变了TextView的字体 但这没有用 这是代码 在 onCreateOptionsMenu 内 getLa
  • Android 图标与徽标

    The
  • Android 可扩展 GridView 就像 Google 图像一样

    我想创建可扩展的 GridView 其行为类似于 Google 图像页面 我想知道如何实现这样的功能 当我单击其中一张图像时 页面会展开并显示图片的详细信息 Android 中是否可以做类似的事情 Images grid Expanded
  • 动画结束后更改视图位置

    我开发了一个基于ViewGroup我的问题是我需要在动画结束后保存项目的位置 我打了电话setFillAfter true 在我创建的动画对象中AnimationListener并在其中onAnimationEnd方法调用View layo
  • 我的 apk 文件在模拟器中的位置

    我在 eclipse android 中编写了一个小程序 现在我安装并运行我的程序 它是一个 apk 现在我想知道我的 apk 文件在哪里 我什至想将它 拉 到我的系统中 是否可以 如果是这样请帮助我 如果您只想将 apk 安装在手机或类似

随机推荐