- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我遇到了 Android 问题 NsdManager按照他们的教程 Using Network Service Discovery .
我的网络上有一些 zeroconf/bonjour 硬件设备。在我的 mac 上,我可以通过以下命令从我的终端中发现所有这些dns-sd -Z _my-mesh._tcp.
从我的 Android 应用程序的第一次运行开始,我可以使用 NsdManager 完美地发现这些服务。但是,如果我重新启动应用程序并再次尝试,则找不到任何服务。 onDiscoveryStarted
被成功调用,但之后没有其他任何事情。在等待期间,我可以从我的 mac 确认服务仍然成功。
然后我可以打开我的 Zeroconf 应用程序(在 Android 上),它会像我的 mac 一样显示服务。当我返回我的应用程序时,我看到它立即收到我之前预期的所有回调。所以我相信我的方法有问题,但我不确定是什么。下面是我用来发现和解析服务的代码。该 View 是一个巨大的 TextView (在 ScrollView 中),我一直在其中写入文本以便于调试。
import android.annotation.SuppressLint
import android.content.Context
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class MainActivity : AppCompatActivity(),
NsdManager.DiscoveryListener {
private var nsdManager: NsdManager? = null
private var text: TextView? = null
private var isResolving = false
private val services = ArrayList<ServiceWrapper>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
this.text = findViewById(R.id.text)
this.nsdManager = application.getSystemService(Context.NSD_SERVICE) as NsdManager
}
override fun onResume() {
super.onResume()
this.nsdManager?.discoverServices("_my-mesh._tcp.", NsdManager.PROTOCOL_DNS_SD, this)
write("Resume Discovering Services")
}
override fun onPause() {
super.onPause()
this.nsdManager?.stopServiceDiscovery(this)
write("Pause Discovering Services")
}
override fun onServiceFound(serviceInfo: NsdServiceInfo?) {
write("onServiceFound(serviceInfo = $serviceInfo))")
if (serviceInfo == null) {
return
}
add(serviceInfo)
}
override fun onStopDiscoveryFailed(serviceType: String?, errorCode: Int) {
write("onStopDiscoveryFailed(serviceType = $serviceType, errorCode = $errorCode)")
}
override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) {
write("onStartDiscoveryFailed(serviceType = $serviceType, errorCode = $errorCode)")
}
override fun onDiscoveryStarted(serviceType: String?) {
write("onDiscoveryStarted(serviceType = $serviceType)")
}
override fun onDiscoveryStopped(serviceType: String?) {
write("onDiscoveryStopped(serviceType = $serviceType)")
}
override fun onServiceLost(serviceInfo: NsdServiceInfo?) {
write("onServiceLost(serviceInfo = $serviceInfo)")
}
private fun createResolveListener(): NsdManager.ResolveListener {
return object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
write("onResolveFailed(serviceInfo = $serviceInfo, errorCode = $errorCode)")
isResolving = false
resolveNext()
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo?) {
write("onServiceResolved(serviceInfo = $serviceInfo)")
if (serviceInfo == null) {
return
}
for (servicewrapper in services) {
if (servicewrapper.serviceInfo.serviceName == serviceInfo.serviceName) {
servicewrapper.resolve(serviceInfo)
}
}
isResolving = false
resolveNext()
}
}
}
@SuppressLint("SetTextI18n")
private fun write(text: String?) {
this.text?.let {
it.post({
it.text = it.text.toString() + "\n" + text + "\n"
})
}
}
fun add(serviceInfo: NsdServiceInfo) {
for (servicewrapper in services) {
if (servicewrapper.serviceInfo.serviceName == serviceInfo.serviceName) {
return
}
}
services.add(ServiceWrapper(serviceInfo))
resolveNext()
}
@Synchronized
fun resolveNext() {
if (isResolving) {
return
}
isResolving = true
for (servicewrapper in services) {
if (servicewrapper.isResolved) {
continue
}
write("resolving")
this.nsdManager?.resolveService(servicewrapper.serviceInfo, createResolveListener())
return
}
isResolving = false
}
inner class ServiceWrapper(var serviceInfo: NsdServiceInfo) {
var isResolved = false
fun resolve(serviceInfo: NsdServiceInfo) {
isResolved = true
this.serviceInfo = serviceInfo
}
}
}
最佳答案
迟到总比不到好。直到现在才意识到其他人也有这个问题。
我们发现一些路由器正在阻止或无法正确地来回转发数据包。我们对此的解决方案是使用 wire shark 来检测其他流行的应用程序正在做什么来解决这个问题。 Android 的 NsdManager 的可定制性有限,因此它需要通过 MulticastSocket 手动传输数据包。
interface NsdDiscovery {
suspend fun startDiscovery()
suspend fun stopDiscovery()
fun setListener(listener: Listener?)
fun isDiscovering(): Boolean
interface Listener {
fun onServiceFound(ip:String, local:String)
fun onServiceLost(event: ServiceEvent)
}
}
@Singleton
class ManualNsdDiscovery @Inject constructor()
: NsdDiscovery {
//region Fields
private val isDiscovering = AtomicBoolean(false)
private var socketManager: SocketManager? = null
private var listener: WeakReference<NsdDiscovery.Listener> = WeakReference<NsdDiscovery.Listener>(null)
//endregion
//region NsdDiscovery
override suspend fun startDiscovery() = withContext(Dispatchers.IO) {
if (isDiscovering()) return@withContext
this@ManualNsdDiscovery.isDiscovering.set(true)
val socketManager = SocketManager()
socketManager.start()
this@ManualNsdDiscovery.socketManager = socketManager
}
override suspend fun stopDiscovery() = withContext(Dispatchers.IO) {
if (!isDiscovering()) return@withContext
this@ManualNsdDiscovery.socketManager?.stop()
this@ManualNsdDiscovery.socketManager = null
this@ManualNsdDiscovery.isDiscovering.set(false)
}
override fun setListener(listener: NsdDiscovery.Listener?) {
this.listener = WeakReference<NsdDiscovery.Listener>(listener)
}
@Synchronized
override fun isDiscovering(): Boolean {
return this.isDiscovering.get()
}
//endregion
private inner class SocketManager {
//region Fields
private val group = InetAddress.getByName("224.0.0.251")
?: throw IllegalStateException("Can't setup group")
private val incomingNsd = IncomingNsd()
private val outgoingNsd = OutgoingNsd()
//endregion
//region Constructors
//endregion
//region Methods
suspend fun start() {
this.incomingNsd.startListening()
this.outgoingNsd.send()
}
fun stop() {
this.incomingNsd.stopListening()
}
//endregion
private inner class OutgoingNsd {
//region Fields
private val socketMutex = Mutex()
private var socket = MulticastSocket(5353)
suspend fun setUpSocket() {
this.socketMutex.withLock {
try {
this.socket = MulticastSocket(5353)
this.socket.reuseAddress = true
this.socket.joinGroup(group)
} catch (e: SocketException) {
return
}
}
}
suspend fun tearDownSocket() {
this.socketMutex.withLock {
this@OutgoingNsd.socket.close()
}
}
//ugly code but here is the packet
private val bytes = byteArrayOf(171.toByte(), 205.toByte(), 1.toByte(), 32.toByte(),
0.toByte(), 1.toByte(), 0.toByte(), 0.toByte(),
0.toByte(), 0.toByte(), 0.toByte(), 0.toByte(),
9.toByte(), 95.toByte(), 101.toByte(), 118.toByte(),
97.toByte(), 45.toByte(), 109.toByte(), 101.toByte(),
115.toByte(), 104.toByte(), 4.toByte(), 95.toByte(),
116.toByte(), 99.toByte(), 112.toByte(), 5.toByte(),
108.toByte(), 111.toByte(), 99.toByte(), 97.toByte(),
108.toByte(), 0.toByte(), 0.toByte(), 12.toByte(),
0.toByte(), 1.toByte())
private val outPacket = DatagramPacket(bytes,
bytes.size,
this@SocketManager.group,
5353)
//endregion
//region Methods
@Synchronized
suspend fun send() {
withContext(Dispatchers.Default) {
setUpSocket()
try {
this@OutgoingNsd.socket.send(this@OutgoingNsd.outPacket)
delay(1500L)
tearDownSocket()
} catch (e: Exception) {
}
}
}
//endregion
}
private inner class IncomingNsd {
//region Fields
private val isRunning = AtomicBoolean(false)
private var socket = MulticastSocket(5353)
//endregion
//region Any
fun setUpSocket() {
try {
this.socket = MulticastSocket(5353)
this.socket.reuseAddress = true
this.socket.joinGroup(group)
} catch (e: SocketException) {
} catch (e: BindException) {
}
}
fun run() {
GlobalScope.launch(Dispatchers.Default) {
setUpSocket()
try {
while (this@IncomingNsd.isRunning.get()) {
val bytes = ByteArray(4096)
val inPacket = DatagramPacket(bytes, bytes.size)
this@IncomingNsd.socket.receive(inPacket)
val incoming = DNSIncoming(inPacket)
for (answer in incoming.allAnswers) {
if (answer.key.contains("_my_mesh._tcp")) {
this@ManualNsdDiscovery.listener.get()?.onServiceFound(answer.recordSource.hostAddress, answer.name)
return@launch
}
}
}
this@IncomingNsd.socket.close()
} catch (e: Exception) {
}
}
}
//endregion
//region Methods
@Synchronized
fun startListening() {
if (this.isRunning.get()) {
return
}
this.isRunning.set(true)
run()
}
@Synchronized
fun stopListening() {
if (!this.isRunning.get()) {
return
}
this.isRunning.set(false)
}
//endregion
}
}
}
关于Android NsdManager 无法发现服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48468030/
我遇到了 Android 问题 NsdManager按照他们的教程 Using Network Service Discovery . 我的网络上有一些 zeroconf/bonjour 硬件设备。在
从例子开始http://developer.android.com/training/connect-devices-wirelessly/nsd.html代码,我一直在构建一个旨在在同一子网中的多个
在我目前正在开发的 Android 应用程序中,我想使用 NsdManager 连接做零配置网络。 我设法运行网络服务发现并连接到所需的网络,但停止发现后 NsdManager 线程仍在运行。这会导致
我在本地网络上运行一个简单的 http 服务器,并尝试使用 Android 设备上的 NsdManager 来发现它。我关注了 Android training guide on this matte
我正在尝试使用 Android 的 NsdManager API 实现一个用于服务发现的类。我想知道如何从主要 Activity 开始一段固定时间的服务搜索,然后停止它以使用搜索结果? NsdMana
我正在尝试使用 NsdManager 在 android 上注册一个 nsdservice,但它总是因为内部错误而失败,不管那是什么意思。这只是一个测试,将来我希望能够将多个设备连接到一个“主机设备”
为了让 NsdManager 发现功能在 Android 9 上运行,我已经尝试了很长时间。它可以在 Android 9 之前的任何 Android 版本上运行,没有任何问题。 出于测试目的,我使用了
我在使用 context.getSystemService(Context.NSD_SERVICE) 时遇到问题。它只是崩溃了。代码如下: 我的网络服务发现助手: public NetworkServ
我正在使用 NsdManager 在本地网络上注册一个 Web 服务。我的问题是设备名称称为“android”,因此我可以从我的笔记本电脑上以“android.local”访问手机。我怎样才能改变这个
我在 Android 应用程序中使用 NsdManager 来发现由我开发的另一台设备发布的 NSD 服务。我只在 Android App 上进行服务发现(这方面不需要服务注册)。网络上同时发布了多个
我正在尝试获取作为参数传递给 NsdManager.DiscoveryListener.onServiceFound() 的 NsdServiceInfo 的 mHost,但它为空。我有两个 andr
在过去的几周中,Android的NSD实现让我发疯了: 从用户的角度来看,会出现以下问题: 设备以完全不确定的方式相互发现。如果我启动基于NsdManager的应用程序,则只要涉及两个设备,它就会或多
我是一名优秀的程序员,十分优秀!