From 9a77733d24e60628b24476ea02159c23cf9b3345 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Mon, 13 Apr 2026 09:00:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(scale):=20=E4=BF=AE=E5=A4=8D=E4=B8=BB?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E6=97=A0=E6=B3=95=E5=8F=91=E7=8E=B0=E5=AD=90?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=8F=8AIP=E6=98=BE=E7=A4=BA=E6=9C=AA?= =?UTF-8?q?=E7=9F=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UdpBroadcastReceiver 补充 socket.broadcast=true,修复广播包被内核丢弃 - MdnsDiscoveryManager 改为串行解析队列,修复多设备并发解析报 FAILURE_ALREADY_ACTIVE - ScaleWebSocketServer 推送数据时动态填充本机 IP,解决子设备 IP 始终显示未知 --- .../dish/match/scale/MdnsDiscoveryManager.kt | 44 ++++++++++++++++++- .../dish/match/scale/ScaleServiceManager.kt | 2 +- .../dish/match/scale/ScaleWebSocketServer.kt | 7 ++- .../dish/match/scale/UdpBroadcastReceiver.kt | 2 +- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/shuwei/dish/match/scale/MdnsDiscoveryManager.kt b/app/src/main/java/com/shuwei/dish/match/scale/MdnsDiscoveryManager.kt index d65af14..fcbddd8 100644 --- a/app/src/main/java/com/shuwei/dish/match/scale/MdnsDiscoveryManager.kt +++ b/app/src/main/java/com/shuwei/dish/match/scale/MdnsDiscoveryManager.kt @@ -4,10 +4,14 @@ import android.content.Context import android.net.nsd.NsdManager import android.net.nsd.NsdServiceInfo import android.util.Log +import java.util.LinkedList /** * mDNS 设备发现管理器(仅主设备运行) * 持续扫描局域网,发现或丢失 DishMatch 子设备时通过回调通知连接管理器 + * + * 注意:Android NsdManager.resolveService 不支持并发调用,多台子设备同时被发现时 + * 会报 FAILURE_ALREADY_ACTIVE(3) 导致解析失败。此处使用串行队列逐一解析。 */ class MdnsDiscoveryManager(private val context: Context) { @@ -22,6 +26,11 @@ class MdnsDiscoveryManager(private val context: Context) { private var discoveryListener: NsdManager.DiscoveryListener? = null private var isDiscovering = false + /** 待解析队列,串行处理避免并发解析失败 */ + private val resolveQueue = LinkedList() + @Volatile + private var isResolving = false + /** * 发现新设备时的回调:(deviceId, host, port) */ @@ -62,7 +71,7 @@ class MdnsDiscoveryManager(private val context: Context) { // 只处理 DishMatch 服务 if (!service.serviceName.startsWith("DishMatch-")) return Log.d(TAG, "发现设备: ${service.serviceName}") - resolveService(service) + enqueueResolve(service) } override fun onServiceLost(service: NsdServiceInfo) { @@ -87,18 +96,45 @@ class MdnsDiscoveryManager(private val context: Context) { nsdManager.resolveService(service, object : NsdManager.ResolveListener { override fun onResolveFailed(info: NsdServiceInfo, errorCode: Int) { Log.e(TAG, "解析设备地址失败: ${info.serviceName}, errorCode=$errorCode") + // 解析结束(失败),继续处理队列中下一个 + processNextResolve() } override fun onServiceResolved(info: NsdServiceInfo) { val deviceId = info.serviceName.removePrefix("DishMatch-") - val host = info.host?.hostAddress ?: return + val host = info.host?.hostAddress ?: run { + processNextResolve() + return + } val port = info.port Log.d(TAG, "设备地址已解析: $deviceId -> $host:$port") onDeviceFound?.invoke(deviceId, host, port) + // 解析结束(成功),继续处理队列中下一个 + processNextResolve() } }) } + /** 将待解析服务加入队列,若当前无解析任务则立即开始 */ + @Synchronized + private fun enqueueResolve(service: NsdServiceInfo) { + resolveQueue.add(service) + if (!isResolving) { + processNextResolve() + } + } + + /** 从队列取出下一个服务进行解析 */ + @Synchronized + private fun processNextResolve() { + val next = resolveQueue.poll() ?: run { + isResolving = false + return + } + isResolving = true + resolveService(next) + } + /** * 停止扫描,在应用退出时调用 */ @@ -112,5 +148,9 @@ class MdnsDiscoveryManager(private val context: Context) { } } discoveryListener = null + synchronized(this) { + resolveQueue.clear() + isResolving = false + } } } diff --git a/app/src/main/java/com/shuwei/dish/match/scale/ScaleServiceManager.kt b/app/src/main/java/com/shuwei/dish/match/scale/ScaleServiceManager.kt index 4e25194..ddf1c1a 100644 --- a/app/src/main/java/com/shuwei/dish/match/scale/ScaleServiceManager.kt +++ b/app/src/main/java/com/shuwei/dish/match/scale/ScaleServiceManager.kt @@ -63,7 +63,7 @@ object ScaleServiceManager { Log.d(TAG, "启动秤服务, deviceId=$deviceId, role=$role") // 所有设备:启动 WebSocket 服务端 + mDNS 注册 - wsServer = ScaleWebSocketServer(deviceId).also { it.start() } + wsServer = ScaleWebSocketServer(deviceId, context).also { it.start() } mdnsRegister = MdnsRegisterManager(context).also { it.register(deviceId) } if (role == DeviceRole.MASTER) { diff --git a/app/src/main/java/com/shuwei/dish/match/scale/ScaleWebSocketServer.kt b/app/src/main/java/com/shuwei/dish/match/scale/ScaleWebSocketServer.kt index 2cb85ab..d4363bb 100644 --- a/app/src/main/java/com/shuwei/dish/match/scale/ScaleWebSocketServer.kt +++ b/app/src/main/java/com/shuwei/dish/match/scale/ScaleWebSocketServer.kt @@ -1,7 +1,9 @@ package com.shuwei.dish.match.scale +import android.content.Context import android.util.Log import com.google.gson.Gson +import com.shuwei.dish.match.utils.NetworkUtil import com.shuwei.dish.match.utils.WeightUtil import org.java_websocket.WebSocket import org.java_websocket.handshake.ClientHandshake @@ -12,7 +14,7 @@ import java.net.InetSocketAddress * 秤数据 WebSocket 服务端(主设备和子设备均运行) * 监听 WeightUtil 回调,将本机秤数据实时推送给所有已连接的主设备客户端 */ -class ScaleWebSocketServer(private val deviceId: String) { +class ScaleWebSocketServer(private val deviceId: String, private val context: Context) { companion object { private const val TAG = "ScaleWebSocketServer" @@ -53,7 +55,8 @@ class ScaleWebSocketServer(private val deviceId: String) { address = address, weight = weight, state = state, - ts = now + ts = now, + ip = NetworkUtil.getLocalIpAddress(context) ) latestData[address] = data broadcast(gson.toJson(data)) diff --git a/app/src/main/java/com/shuwei/dish/match/scale/UdpBroadcastReceiver.kt b/app/src/main/java/com/shuwei/dish/match/scale/UdpBroadcastReceiver.kt index 379e6a3..50f1f44 100644 --- a/app/src/main/java/com/shuwei/dish/match/scale/UdpBroadcastReceiver.kt +++ b/app/src/main/java/com/shuwei/dish/match/scale/UdpBroadcastReceiver.kt @@ -47,7 +47,7 @@ class UdpBroadcastReceiver { private fun listenLoop() { try { - socket = DatagramSocket(UdpBroadcastSender.BROADCAST_PORT) + socket = DatagramSocket(UdpBroadcastSender.BROADCAST_PORT).also { it.broadcast = true } val buf = ByteArray(BUFFER_SIZE) while (running) { val packet = DatagramPacket(buf, buf.size)