AeroToss: исправлен broadcast адрес + логирование

- getBroadcastAddress() вычисляет реальный broadcast адрес подсети
- Логирование: TCP подключения, UDP broadcast, найденные устройства
- Логирование broadcast ошибок для отладки
This commit is contained in:
2026-08-19 23:37:52 +03:00
parent 827d431311
commit fe880cbac1
@@ -37,12 +37,12 @@ class TcpDiscovery(
override fun startDiscovery(servicePort: Int) { override fun startDiscovery(servicePort: Int) {
if (serverThread != null) return if (serverThread != null) return
// TCP server
serverThread = Thread { serverThread = Thread {
try { try {
val ss = ServerSocket(TCP_PORT, 50, InetAddress.getByName("0.0.0.0")) val ss = ServerSocket(TCP_PORT, 50, InetAddress.getByName("0.0.0.0"))
serverSocket = ss 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) { while (!Thread.currentThread().isInterrupted) {
try { try {
val client = ss.accept() val client = ss.accept()
@@ -53,17 +53,19 @@ class TcpDiscovery(
} }
ss.close() ss.close()
} catch (e: Exception) { } 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() } }.apply { isDaemon = true; name = "aerotoss-tcp"; start() }
// UDP broadcast
discoveryThread = Thread { discoveryThread = Thread {
try { try {
val socket = DatagramSocket(UDP_PORT, InetAddress.getByName("0.0.0.0")) val socket = DatagramSocket(UDP_PORT, InetAddress.getByName("0.0.0.0"))
socket.broadcast = true socket.broadcast = true
socket.soTimeout = 2000 socket.soTimeout = 2000
udpSocket = socket udpSocket = socket
println("AeroToss: UDP broadcast port=$UDP_PORT")
var lastBroadcast = System.currentTimeMillis() var lastBroadcast = System.currentTimeMillis()
while (!Thread.currentThread().isInterrupted) { while (!Thread.currentThread().isInterrupted) {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
@@ -75,6 +77,8 @@ class TcpDiscovery(
try { try {
val pkt = DatagramPacket(buf, buf.size) val pkt = DatagramPacket(buf, buf.size)
socket.receive(pkt) socket.receive(pkt)
val from = pkt.address.hostAddress
println("AeroToss: UDP received from $from")
onBroadcastReceived(pkt, servicePort) onBroadcastReceived(pkt, servicePort)
} catch (_: java.net.SocketTimeoutException) {} } catch (_: java.net.SocketTimeoutException) {}
} }
@@ -88,10 +92,13 @@ class TcpDiscovery(
private fun handleRequest(client: Socket, servicePort: Int) { private fun handleRequest(client: Socket, servicePort: Int) {
try { try {
client.use { sock -> client.use { sock ->
val from = sock.inetAddress?.hostAddress ?: "unknown"
println("AeroToss: TCP connection from $from")
sock.soTimeout = 5000 sock.soTimeout = 5000
val reader = sock.getInputStream().bufferedReader() val reader = sock.getInputStream().bufferedReader()
val writer = sock.getOutputStream().bufferedWriter() val writer = sock.getOutputStream().bufferedWriter()
val line = reader.readLine() ?: return val line = reader.readLine() ?: return
println("AeroToss: TCP request: $line")
if (line == "DISCOVER") { if (line == "DISCOVER") {
val resp = DiscoveryResponse( val resp = DiscoveryResponse(
id = deviceId, id = deviceId,
@@ -100,20 +107,48 @@ class TcpDiscovery(
port = servicePort, port = servicePort,
ip = findMyIp() ip = findMyIp()
) )
writer.write(json.encodeToString(resp)) val jsonStr = json.encodeToString(resp)
writer.write(jsonStr)
writer.newLine() writer.newLine()
writer.flush() 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) { private fun broadcast(socket: DatagramSocket, servicePort: Int) {
try { try {
val msg = "AEROTOSS:$TCP_PORT:$servicePort:$deviceId:$deviceName" 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) 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) {} } 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) { private fun onBroadcastReceived(pkt: DatagramPacket, myServicePort: Int) {
@@ -130,13 +165,18 @@ class TcpDiscovery(
if (remoteServicePort == myServicePort) return if (remoteServicePort == myServicePort) return
val host = pkt.address.hostAddress ?: return val host = pkt.address.hostAddress ?: return
println("AeroToss: Broadcast from $host:$remoteTcpPort name=$remoteName")
val device = queryRemote(host, remoteTcpPort) val device = queryRemote(host, remoteTcpPort)
if (device != null) { if (device != null) {
discoveredDevices[device.id] = device discoveredDevices[device.id] = device
_devices.value = discoveredDevices.values.toList() _devices.value = discoveredDevices.values.toList()
println("AeroToss: Found ${device.name} [${device.type}] at ${device.hostAddress}:${device.port}") 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? { private fun queryRemote(host: String, tcpPort: Int): Device? {
@@ -150,27 +190,36 @@ class TcpDiscovery(
writer.newLine() writer.newLine()
writer.flush() writer.flush()
val line = reader.readLine() ?: return null val line = reader.readLine() ?: return null
val resp = json.decodeFromString<DiscoveryResponse>(line) json.decodeFromString<DiscoveryResponse>(line).let { resp ->
Device( Device(
id = resp.id, id = resp.id,
name = resp.name, name = resp.name,
type = if (resp.type == "desktop") DeviceType.DESKTOP else DeviceType.PHONE, type = if (resp.type == "desktop") DeviceType.DESKTOP else DeviceType.PHONE,
hostAddress = resp.ip, hostAddress = resp.ip,
port = resp.port port = resp.port
) )
}
} }
} catch (_: Exception) { null } } catch (e: Exception) {
println("AeroToss: queryRemote $host:$tcpPort failed: ${e.message}")
null
}
} }
private fun findMyIp(): String { private fun findMyIp(): String {
return try { try {
NetworkInterface.getNetworkInterfaces()?.toList() val interfaces = NetworkInterface.getNetworkInterfaces()
?.filter { it.isUp && !it.isLoopback } while (interfaces.hasMoreElements()) {
?.flatMap { it.inetAddresses.toList() } val iface = interfaces.nextElement()
?.filterIsInstance<java.net.Inet4Address>() if (iface.isLoopback || !iface.isUp) continue
?.firstOrNull { isPrivate(it) } for (addr in iface.inetAddresses) {
?.hostAddress ?: "127.0.0.1" if (addr is java.net.Inet4Address && isPrivate(addr)) {
} catch (_: Exception) { "127.0.0.1" } return addr.hostAddress
}
}
}
} catch (_: Exception) {}
return "127.0.0.1"
} }
private fun isPrivate(addr: java.net.InetAddress): Boolean { private fun isPrivate(addr: java.net.InetAddress): Boolean {