AeroToss: исправлен UDP broadcast (слушает порт 9999)
- UDP socket теперь слушает на порту 9999 (а не на случайном) - Оба устройства отправляют и слушают broadcast на одном порту - findNetworkAddress() определяет приватный IP автоматически - hostAddress возвращает реальный IP而非127.0.0.1
This commit is contained in:
@@ -23,6 +23,7 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
override val devices: Flow<List<Device>> = _devices.asStateFlow()
|
override val devices: Flow<List<Device>> = _devices.asStateFlow()
|
||||||
|
|
||||||
private var serverSocket: ServerSocket? = null
|
private var serverSocket: ServerSocket? = null
|
||||||
|
private var udpSocket: DatagramSocket? = null
|
||||||
private var serverThread: Thread? = null
|
private var serverThread: Thread? = null
|
||||||
private var discoveryThread: Thread? = null
|
private var discoveryThread: Thread? = null
|
||||||
private val deviceId = generateDeviceId()
|
private val deviceId = generateDeviceId()
|
||||||
@@ -33,41 +34,34 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
override fun startDiscovery(servicePort: Int) {
|
override fun startDiscovery(servicePort: Int) {
|
||||||
if (serverThread != null) return
|
if (serverThread != null) return
|
||||||
|
|
||||||
// Start TCP server for incoming discovery requests
|
// TCP server for incoming discovery requests
|
||||||
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: Server listening on port $TCP_PORT")
|
println("AeroToss: TCP server on port $TCP_PORT, service on $servicePort")
|
||||||
|
|
||||||
while (!Thread.currentThread().isInterrupted) {
|
while (!Thread.currentThread().isInterrupted) {
|
||||||
try {
|
try {
|
||||||
val client = ss.accept()
|
val client = ss.accept()
|
||||||
Thread {
|
Thread { handleDiscoveryRequest(client, servicePort) }.start()
|
||||||
handleDiscoveryRequest(client, servicePort)
|
|
||||||
}.start()
|
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
if (Thread.currentThread().isInterrupted) break
|
if (Thread.currentThread().isInterrupted) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ss.close()
|
ss.close()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (!Thread.currentThread().isInterrupted) {
|
if (!Thread.currentThread().isInterrupted) println("AeroToss: Server error: ${e.message}")
|
||||||
println("AeroToss-TCP: Server error: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.apply {
|
}.apply { isDaemon = true; name = "aerotoss-server"; start() }
|
||||||
isDaemon = true
|
|
||||||
name = "aerotoss-tcp-server"
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start UDP broadcast sender
|
// UDP broadcast: send AND receive on same socket
|
||||||
discoveryThread = Thread {
|
discoveryThread = Thread {
|
||||||
try {
|
try {
|
||||||
val socket = DatagramSocket()
|
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
|
||||||
|
|
||||||
var lastBroadcast = System.currentTimeMillis()
|
var lastBroadcast = System.currentTimeMillis()
|
||||||
|
|
||||||
@@ -78,7 +72,6 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
lastBroadcast = now
|
lastBroadcast = now
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for broadcasts from other devices
|
|
||||||
val buf = ByteArray(1024)
|
val buf = ByteArray(1024)
|
||||||
try {
|
try {
|
||||||
val packet = DatagramPacket(buf, buf.size)
|
val packet = DatagramPacket(buf, buf.size)
|
||||||
@@ -88,15 +81,9 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
}
|
}
|
||||||
socket.close()
|
socket.close()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (!Thread.currentThread().isInterrupted) {
|
if (!Thread.currentThread().isInterrupted) println("AeroToss: UDP error: ${e.message}")
|
||||||
println("AeroToss-TCP: Broadcast error: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.apply {
|
}.apply { isDaemon = true; name = "aerotoss-broadcast"; start() }
|
||||||
isDaemon = true
|
|
||||||
name = "aerotoss-tcp-broadcast"
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDiscoveryRequest(client: Socket, servicePort: Int) {
|
private fun handleDiscoveryRequest(client: Socket, servicePort: Int) {
|
||||||
@@ -123,30 +110,11 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findNetworkAddress(): String {
|
|
||||||
return try {
|
|
||||||
NetworkInterface.getNetworkInterfaces()?.toList()
|
|
||||||
?.filter { it.isUp && !it.isLoopback }
|
|
||||||
?.flatMap { it.inetAddresses.toList() }
|
|
||||||
?.filterIsInstance<java.net.Inet4Address>()
|
|
||||||
?.firstOrNull { isPrivate(it) }
|
|
||||||
?.hostAddress ?: "127.0.0.1"
|
|
||||||
} catch (_: Exception) { "127.0.0.1" }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isPrivate(addr: java.net.InetAddress): Boolean {
|
|
||||||
val bytes = addr.address
|
|
||||||
return (bytes[0].toInt() and 0xFF == 192 && bytes[1].toInt() and 0xFF == 168) ||
|
|
||||||
(bytes[0].toInt() and 0xFF == 10) ||
|
|
||||||
(bytes[0].toInt() and 0xFF == 172 && (bytes[1].toInt() and 0xFF in 16..31))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendBroadcast(socket: DatagramSocket, servicePort: Int) {
|
private fun sendBroadcast(socket: DatagramSocket, servicePort: Int) {
|
||||||
try {
|
try {
|
||||||
val message = "AEROTOSS:$TCP_PORT:$servicePort:$deviceId:$deviceName"
|
val message = "AEROTOSS:$TCP_PORT:$servicePort:$deviceId:$deviceName"
|
||||||
val data = message.toByteArray(Charsets.UTF_8)
|
val data = message.toByteArray(Charsets.UTF_8)
|
||||||
val broadcastAddr = InetAddress.getByName("255.255.255.255")
|
val packet = DatagramPacket(data, data.size, InetAddress.getByName("255.255.255.255"), UDP_PORT)
|
||||||
val packet = DatagramPacket(data, data.size, broadcastAddr, UDP_PORT)
|
|
||||||
socket.send(packet)
|
socket.send(packet)
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
@@ -167,14 +135,13 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
if (remoteId == deviceId) return
|
if (remoteId == deviceId) return
|
||||||
if (remoteServicePort == myPort) return
|
if (remoteServicePort == myPort) return
|
||||||
|
|
||||||
// Connect to remote device to get full info
|
|
||||||
val hostAddress = packet.address.hostAddress ?: return
|
val hostAddress = packet.address.hostAddress ?: return
|
||||||
val device = queryDevice(hostAddress, remoteTcpPort, remoteId, remoteName, remoteServicePort)
|
val device = queryDevice(hostAddress, remoteTcpPort, remoteId, remoteName, remoteServicePort)
|
||||||
|
|
||||||
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-TCP: Found ${device.name} at ${device.hostAddress}:${device.port}")
|
println("AeroToss: Found ${device.name} at ${device.hostAddress}:${device.port}")
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
@@ -204,7 +171,6 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
// Fallback to broadcast info
|
|
||||||
Device(
|
Device(
|
||||||
id = fallbackId,
|
id = fallbackId,
|
||||||
name = fallbackName,
|
name = fallbackName,
|
||||||
@@ -215,13 +181,33 @@ class TcpDiscovery : DiscoveryManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun findNetworkAddress(): String {
|
||||||
|
return try {
|
||||||
|
NetworkInterface.getNetworkInterfaces()?.toList()
|
||||||
|
?.filter { it.isUp && !it.isLoopback }
|
||||||
|
?.flatMap { it.inetAddresses.toList() }
|
||||||
|
?.filterIsInstance<java.net.Inet4Address>()
|
||||||
|
?.firstOrNull { isPrivate(it) }
|
||||||
|
?.hostAddress ?: "127.0.0.1"
|
||||||
|
} catch (_: Exception) { "127.0.0.1" }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isPrivate(addr: java.net.InetAddress): Boolean {
|
||||||
|
val bytes = addr.address
|
||||||
|
return (bytes[0].toInt() and 0xFF == 192 && bytes[1].toInt() and 0xFF == 168) ||
|
||||||
|
(bytes[0].toInt() and 0xFF == 10) ||
|
||||||
|
(bytes[0].toInt() and 0xFF == 172 && (bytes[1].toInt() and 0xFF in 16..31))
|
||||||
|
}
|
||||||
|
|
||||||
override fun stopDiscovery() {
|
override fun stopDiscovery() {
|
||||||
serverThread?.interrupt()
|
serverThread?.interrupt()
|
||||||
serverThread = null
|
serverThread = null
|
||||||
discoveryThread?.interrupt()
|
discoveryThread?.interrupt()
|
||||||
discoveryThread = null
|
discoveryThread = null
|
||||||
try { serverSocket?.close() } catch (_: Exception) {}
|
try { serverSocket?.close() } catch (_: Exception) {}
|
||||||
|
try { udpSocket?.close() } catch (_: Exception) {}
|
||||||
serverSocket = null
|
serverSocket = null
|
||||||
|
udpSocket = null
|
||||||
discoveredDevices.clear()
|
discoveredDevices.clear()
|
||||||
_devices.value = emptyList()
|
_devices.value = emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user