-
-
Save hhhaiai/498217708b358b64b012809946da6943 to your computer and use it in GitHub Desktop.
通过 UDP 广播局域网自动发现 Android 客户端 IP / Auto find android device ip by UDP broadcast
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Receive desptop`s udp broadcast package on Android, let Android app known desktop device`s ip | |
package top.gtf35.nekosdk.network | |
import kotlinx.coroutines.* | |
import top.gtf35.nekosdk.utils.LogUtil | |
import java.io.BufferedReader | |
import java.io.InputStreamReader | |
import java.net.DatagramPacket | |
import java.net.DatagramSocket | |
import java.net.InetAddress | |
import java.net.SocketException | |
private const val LISTENING_PORT = 9999 | |
private const val BUFFER_SIZE = 512 | |
fun main() { | |
val udpNetwork = UDPReceiverNetwork { _: InetAddress, _: Int, _: String->} | |
udpNetwork.startFind() | |
@Suppress("ControlFlowWithEmptyBody") | |
while (true) { | |
val stdin = BufferedReader( | |
InputStreamReader(System.`in`) | |
) | |
val outMessage = stdin.readLine() | |
if (outMessage == "stop") { | |
udpNetwork.stopFind() | |
} | |
if (outMessage == "start") { | |
udpNetwork.startFind() | |
} | |
} | |
} | |
class UDPReceiverNetwork(private val callback : (address: InetAddress, port: Int, msg: String)->Unit) { | |
private lateinit var job: Job | |
private lateinit var socket: DatagramSocket | |
var errorCallback : (e: Throwable)->Unit = {_ ->} | |
private val handler = CoroutineExceptionHandler { _, exception -> | |
CoroutineScope(Dispatchers.Main).launch { | |
LogUtil.w("UDP finder meet exception") | |
exception.printStackTrace() | |
errorCallback.invoke(exception) | |
} | |
} | |
fun startFind() { | |
CoroutineScope(Dispatchers.Main).launch(handler) { | |
uDPFindJob() | |
} | |
} | |
@Suppress("BlockingMethodInNonBlockingContext") | |
private suspend fun uDPFindJob() = withContext(Dispatchers.IO) { | |
socket = DatagramSocket(LISTENING_PORT) | |
val buffer = ByteArray(BUFFER_SIZE) | |
val msg = DatagramPacket(buffer, buffer.size) | |
LogUtil.d("Start listen udp at $LISTENING_PORT") | |
while (isActive) { | |
try { | |
socket.receive(msg) | |
} catch (e: SocketException) { | |
continue | |
} | |
val msgBody = String(msg.data, msg.offset, msg.length) | |
val senderPort = msg.port | |
val senderAddress = msg.address | |
LogUtil.d("Get udp msg from $senderAddress:$senderPort -> $msgBody") | |
withContext(Dispatchers.Main) { | |
callback.invoke(senderAddress, senderPort, msgBody) | |
} | |
} | |
} | |
fun stopFind() { | |
if (this::socket.isInitialized) socket.close() | |
if (this::job.isInitialized) job.cancel() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Send udp broadcast package to desktop, let desktop App known Android device`s ip | |
package top.gtf35.nekosdk.network | |
import android.content.Context | |
import android.net.wifi.WifiManager | |
import kotlinx.coroutines.* | |
import top.gtf35.nekosdk.utils.LogUtil | |
import java.net.DatagramPacket | |
import java.net.DatagramSocket | |
import java.net.InetAddress | |
private const val HOST_LISTENING_PORT = 9999 | |
class UDPSenderNetwork(private val callback : (isSuccess: Boolean, e: Throwable?)->Unit) { | |
private val handler = CoroutineExceptionHandler { _, exception -> | |
CoroutineScope(Dispatchers.Main).launch { | |
LogUtil.d("UDP sender meet exception") | |
exception.printStackTrace() | |
callback.invoke(false, exception) | |
} | |
} | |
fun sendMsg(context: Context, msg: String) { | |
CoroutineScope(Dispatchers.Main).launch(handler) { | |
uDPSendJob(context, msg) | |
} | |
} | |
@Suppress("BlockingMethodInNonBlockingContext") | |
private suspend fun uDPSendJob(context: Context, msgStr: String) = withContext(Dispatchers.IO) { | |
val socket = DatagramSocket() | |
val buffer = msgStr.toByteArray() | |
val address = getBroadcastAddress(context) | |
val msg = DatagramPacket(buffer, buffer.size, address, HOST_LISTENING_PORT) | |
LogUtil.d("UDP send ${String(msg.data)} to ${msg.address}:${msg.port}") | |
socket.send(msg) | |
LogUtil.d("UDP send complete") | |
withContext(Dispatchers.Main) { | |
callback.invoke(true, null) | |
} | |
} | |
} | |
private fun getBroadcastAddress(context: Context): InetAddress? { | |
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager | |
val infoDHCP = wifiManager.dhcpInfo ?: return null | |
val broadcast: Int = infoDHCP.ipAddress and infoDHCP.netmask or infoDHCP.netmask.inv() | |
val quads = ByteArray(4) | |
for (k in 0..3) quads[k] = (broadcast shr k * 8).toByte() | |
return try { | |
InetAddress.getByAddress(quads) | |
} catch (e: Exception) { | |
e.printStackTrace() | |
null | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Receive Android`s udp broadcast package on desktop, let desktop app known Android device`s ip | |
package top.gtf35.nekohelper.network | |
import kotlinx.coroutines.* | |
import mu.KotlinLogging | |
import java.io.BufferedReader | |
import java.io.InputStreamReader | |
import java.net.DatagramPacketUDPReceiverNetwork.kt | |
import java.net.DatagramSocket | |
import java.net.InetAddress | |
import java.net.SocketException | |
private const val LISTENING_PORT = 9999 | |
private const val BUFFER_SIZE = 512 | |
fun main() { | |
val udpNetwork = UDPReceiveNetwork { _: InetAddress, _: Int, _: String->} | |
udpNetwork.startFind() | |
@Suppress("ControlFlowWithEmptyBody") | |
while (true) { | |
val stdin = BufferedReader( | |
InputStreamReader(System.`in`) | |
) | |
val outMessage = stdin.readLine() | |
if (outMessage == "stop") { | |
udpNetwork.stopFind() | |
} | |
if (outMessage == "start") { | |
udpNetwork.startFind() | |
} | |
} | |
} | |
class UDPReceiveNetwork(private val callback : (address: InetAddress, port: Int, msg: String)->Unit) { | |
private val logger = KotlinLogging.logger {} | |
private lateinit var job: Job | |
private lateinit var socket: DatagramSocket | |
private var errorCallback : (e: Throwable)->Unit = {_ ->} | |
private val handler = CoroutineExceptionHandler { _, exception -> | |
CoroutineScope(Dispatchers.Main).launch { | |
logger.warn(exception) { "UDP finder meet exception" } | |
exception.printStackTrace() | |
errorCallback.invoke(exception) | |
} | |
} | |
fun startFind() { | |
CoroutineScope(Dispatchers.Main).launch(handler) { | |
uDPFindJob() | |
} | |
} | |
@Suppress("BlockingMethodInNonBlockingContext") | |
private suspend fun uDPFindJob() = withContext(Dispatchers.IO) { | |
socket = DatagramSocket(LISTENING_PORT) | |
val buffer = ByteArray(BUFFER_SIZE) | |
val msg = DatagramPacket(buffer, buffer.size) | |
logger.debug { "Start listen udp at $LISTENING_PORT" } | |
while (isActive) { | |
try { | |
socket.receive(msg) | |
} catch (e: SocketException){ | |
continue | |
} | |
val msgBody = String(msg.data, msg.offset, msg.length) | |
val senderPort = msg.port | |
val senderAddress = msg.address | |
logger.debug { "Get udp msg from $senderAddress:$senderPort -> $msgBody" } | |
withContext(Dispatchers.Main) { | |
callback.invoke(senderAddress, senderPort, msgBody) | |
} | |
} | |
} | |
fun stopFind() { | |
if (this::socket.isInitialized) socket.close() | |
if (this::job.isInitialized) job.cancel() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Send udp broadcast package to Android, let Android App known desktop device`s ip | |
package top.gtf35.nekohelper.network | |
import kotlinx.coroutines.* | |
import mu.KotlinLogging | |
import java.io.BufferedReader | |
import java.io.InputStreamReader | |
import java.net.* | |
private const val HOST_LISTENING_PORT = 9999 | |
fun main() { | |
for (address in getBroadcastAddress().withIndex()) { | |
KotlinLogging.logger{}.debug { "broadcast address ${address.index} is ${address.value.hostAddress}" } | |
} | |
val udpNetwork = UDPSenderNetwork { isSuccess: Boolean, e: Throwable? -> | |
KotlinLogging.logger{}.debug { "broadcast sent ${if (isSuccess) "success" else "fail"} ${e?.toString() ?: ""}" } | |
} | |
val stdin = BufferedReader( | |
InputStreamReader(System.`in`) | |
) | |
while (true) { | |
udpNetwork.sendMsg(stdin.readLine()) | |
} | |
} | |
class UDPSenderNetwork(private val callback : (isSuccess: Boolean, e: Throwable?)->Unit) { | |
private val logger = KotlinLogging.logger {} | |
private val handler = CoroutineExceptionHandler { _, exception -> | |
CoroutineScope(Dispatchers.Main).launch { | |
logger.debug { "UDP sender meet exception" } | |
exception.printStackTrace() | |
callback.invoke(false, exception) | |
} | |
} | |
fun sendMsg(msg: String) { | |
CoroutineScope(Dispatchers.IO).launch(handler) { | |
for (address in getBroadcastAddress()) sendUDPtoAddressJob(msg, address) | |
} | |
} | |
@Suppress("BlockingMethodInNonBlockingContext") | |
private suspend fun sendUDPtoAddressJob(msgStr: String, address: InetAddress) = withContext(Dispatchers.IO) { | |
val socket = DatagramSocket() | |
val buffer = msgStr.toByteArray() | |
val msg = DatagramPacket(buffer, buffer.size, address, HOST_LISTENING_PORT) | |
logger.debug { "UDP send ${String(msg.data)} to ${msg.address}:${msg.port}" } | |
socket.send(msg) | |
logger.debug { "UDP send complete" } | |
withContext(Dispatchers.Main) { | |
callback.invoke(true, null) | |
} | |
} | |
} | |
private fun getBroadcastAddress(): ArrayList<InetAddress> { | |
val result = ArrayList<InetAddress>() | |
val interfaces = NetworkInterface.getNetworkInterfaces() | |
while (interfaces.hasMoreElements()) { | |
val cur = interfaces.nextElement() | |
if (cur.isLoopback) continue | |
for (interfaceAddress in cur.interfaceAddresses) { | |
if (interfaceAddress.address is Inet4Address) result.add(interfaceAddress.broadcast) | |
} | |
} | |
return result | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment