From fe880cbac110feeaea2e3f9f39d1c6ed060f6641 Mon Sep 17 00:00:00 2001 From: grendervill Date: Wed, 19 Aug 2026 23:37:52 +0300 Subject: [PATCH] =?UTF-8?q?AeroToss:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20broadcast=20=D0=B0=D0=B4=D1=80=D0=B5?= =?UTF-8?q?=D1=81=20+=20=D0=BB=D0=BE=D0=B3=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getBroadcastAddress() вычисляет реальный broadcast адрес подсети - Логирование: TCP подключения, UDP broadcast, найденные устройства - Логирование broadcast ошибок для отладки --- .../com/aerotoss/discovery/TcpDiscovery.kt | 99 ++++++++++++++----- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/app/src/commonMain/kotlin/com/aerotoss/discovery/TcpDiscovery.kt b/app/src/commonMain/kotlin/com/aerotoss/discovery/TcpDiscovery.kt index bf70ab9..c1d4be9 100644 --- a/app/src/commonMain/kotlin/com/aerotoss/discovery/TcpDiscovery.kt +++ b/app/src/commonMain/kotlin/com/aerotoss/discovery/TcpDiscovery.kt @@ -37,12 +37,12 @@ class TcpDiscovery( override fun startDiscovery(servicePort: Int) { if (serverThread != null) return - // TCP server serverThread = Thread { try { val ss = ServerSocket(TCP_PORT, 50, InetAddress.getByName("0.0.0.0")) serverSocket = ss - println("AeroToss: TCP=$TCP_PORT, service=$servicePort, type=$myDeviceType, name=$deviceName") + val myIp = findMyIp() + println("AeroToss: TCP server=$TCP_PORT service=$servicePort type=$myDeviceType ip=$myIp name=$deviceName") while (!Thread.currentThread().isInterrupted) { try { val client = ss.accept() @@ -53,17 +53,19 @@ class TcpDiscovery( } ss.close() } catch (e: Exception) { - if (!Thread.currentThread().isInterrupted) println("AeroToss: Server error: ${e.message}") + if (!Thread.currentThread().isInterrupted) println("AeroToss: TCP server error: ${e.message}") } }.apply { isDaemon = true; name = "aerotoss-tcp"; start() } - // UDP broadcast discoveryThread = Thread { try { val socket = DatagramSocket(UDP_PORT, InetAddress.getByName("0.0.0.0")) socket.broadcast = true socket.soTimeout = 2000 udpSocket = socket + + println("AeroToss: UDP broadcast port=$UDP_PORT") + var lastBroadcast = System.currentTimeMillis() while (!Thread.currentThread().isInterrupted) { val now = System.currentTimeMillis() @@ -75,6 +77,8 @@ class TcpDiscovery( try { val pkt = DatagramPacket(buf, buf.size) socket.receive(pkt) + val from = pkt.address.hostAddress + println("AeroToss: UDP received from $from") onBroadcastReceived(pkt, servicePort) } catch (_: java.net.SocketTimeoutException) {} } @@ -88,10 +92,13 @@ class TcpDiscovery( private fun handleRequest(client: Socket, servicePort: Int) { try { client.use { sock -> + val from = sock.inetAddress?.hostAddress ?: "unknown" + println("AeroToss: TCP connection from $from") sock.soTimeout = 5000 val reader = sock.getInputStream().bufferedReader() val writer = sock.getOutputStream().bufferedWriter() val line = reader.readLine() ?: return + println("AeroToss: TCP request: $line") if (line == "DISCOVER") { val resp = DiscoveryResponse( id = deviceId, @@ -100,20 +107,48 @@ class TcpDiscovery( port = servicePort, ip = findMyIp() ) - writer.write(json.encodeToString(resp)) + val jsonStr = json.encodeToString(resp) + writer.write(jsonStr) writer.newLine() writer.flush() + println("AeroToss: TCP response: $jsonStr") } } - } catch (_: Exception) {} + } catch (e: Exception) { + println("AeroToss: TCP handler error: ${e.message}") + } } private fun broadcast(socket: DatagramSocket, servicePort: Int) { try { val msg = "AEROTOSS:$TCP_PORT:$servicePort:$deviceId:$deviceName" - val pkt = DatagramPacket(msg.toByteArray(Charsets.UTF_8), msg.length, InetAddress.getByName("255.255.255.255"), UDP_PORT) + val data = msg.toByteArray(Charsets.UTF_8) + val broadcastAddr = getBroadcastAddress() + println("AeroToss: Broadcasting to $broadcastAddr:$UDP_PORT") + val pkt = DatagramPacket(data, data.size, broadcastAddr, UDP_PORT) socket.send(pkt) + } catch (e: Exception) { + println("AeroToss: Broadcast error: ${e.message}") + } + } + + private fun getBroadcastAddress(): InetAddress { + try { + val interfaces = NetworkInterface.getNetworkInterfaces() + while (interfaces.hasMoreElements()) { + val iface = interfaces.nextElement() + if (iface.isLoopback || !iface.isUp) continue + for (interfaceAddress in iface.interfaceAddresses) { + val broadcast = interfaceAddress.broadcast + if (broadcast != null && interfaceAddress.address is java.net.Inet4Address) { + println("AeroToss: Found broadcast ${broadcast.hostAddress} on ${iface.name}") + return broadcast + } + } + } } catch (_: Exception) {} + println("AeroToss: Using default broadcast 255.255.255.255") + return InetAddress.getByName("255.255.255.255") } private fun onBroadcastReceived(pkt: DatagramPacket, myServicePort: Int) { @@ -130,13 +165,18 @@ class TcpDiscovery( if (remoteServicePort == myServicePort) return val host = pkt.address.hostAddress ?: return + println("AeroToss: Broadcast from $host:$remoteTcpPort name=$remoteName") val device = queryRemote(host, remoteTcpPort) if (device != null) { discoveredDevices[device.id] = device _devices.value = discoveredDevices.values.toList() println("AeroToss: Found ${device.name} [${device.type}] at ${device.hostAddress}:${device.port}") + } else { + println("AeroToss: Failed to query $host:$remoteTcpPort") } - } catch (_: Exception) {} + } catch (e: Exception) { + println("AeroToss: Broadcast processing error: ${e.message}") + } } private fun queryRemote(host: String, tcpPort: Int): Device? { @@ -150,27 +190,36 @@ class TcpDiscovery( writer.newLine() writer.flush() val line = reader.readLine() ?: return null - val resp = json.decodeFromString(line) - Device( - id = resp.id, - name = resp.name, - type = if (resp.type == "desktop") DeviceType.DESKTOP else DeviceType.PHONE, - hostAddress = resp.ip, - port = resp.port - ) + json.decodeFromString(line).let { resp -> + Device( + id = resp.id, + name = resp.name, + type = if (resp.type == "desktop") DeviceType.DESKTOP else DeviceType.PHONE, + hostAddress = resp.ip, + port = resp.port + ) + } } - } catch (_: Exception) { null } + } catch (e: Exception) { + println("AeroToss: queryRemote $host:$tcpPort failed: ${e.message}") + null + } } private fun findMyIp(): String { - return try { - NetworkInterface.getNetworkInterfaces()?.toList() - ?.filter { it.isUp && !it.isLoopback } - ?.flatMap { it.inetAddresses.toList() } - ?.filterIsInstance() - ?.firstOrNull { isPrivate(it) } - ?.hostAddress ?: "127.0.0.1" - } catch (_: Exception) { "127.0.0.1" } + try { + val interfaces = NetworkInterface.getNetworkInterfaces() + while (interfaces.hasMoreElements()) { + val iface = interfaces.nextElement() + if (iface.isLoopback || !iface.isUp) continue + for (addr in iface.inetAddresses) { + if (addr is java.net.Inet4Address && isPrivate(addr)) { + return addr.hostAddress + } + } + } + } catch (_: Exception) {} + return "127.0.0.1" } private fun isPrivate(addr: java.net.InetAddress): Boolean {