Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save hhhaiai/498217708b358b64b012809946da6943 to your computer and use it in GitHub Desktop.
Save hhhaiai/498217708b358b64b012809946da6943 to your computer and use it in GitHub Desktop.
通过 UDP 广播局域网自动发现 Android 客户端 IP / Auto find android device ip by UDP broadcast
// 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()
}
}
// 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
}
}
// 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()
}
}
// 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