This commit is contained in:
2025-09-29 06:30:39 +08:00
parent 20c229bfb7
commit 5e692bac6c
7 files changed
+138 -66

No files matched your search

+21 -16
View File
@@ -1,9 +1,9 @@
# native-mdns
A Kotlin multiplatform library for mdns announcer(mdns server), only broadcast is supported,
it depends on ktor-network
it depends on ktor-network.
support PTR.
Also, nmdns can be compiled to shared/static lib for other language usage, see [build.gradle.kts](build.gradle.kts)
Supported platforms:
1. windows64(mingwx64)
@@ -12,6 +12,11 @@ Supported platforms:
4. macosArm64
5. jvm(11 or later)
Supported srv type
1. A
2. PTR
> AAAA is not supported
AirPlay2(_airplay._tcp.local.) is tested
@@ -35,7 +40,7 @@ kotlin {
sourceSets {
commonMain.dependencies {
implementation("cn.rtast.nmdns:native-mdns:0.0.1")
implementation("cn.rtast.nmdns:native-mdns:0.0.2")
}
}
}
@@ -44,17 +49,17 @@ kotlin {
There is a simple airplay2 service
```kotlin
val service = createService(
serviceType = "_airplay._tcp.local.",
serviceName = "AP@RTAST",
hostname = "rtakland.local.",
ipAddress = "192.168.10.104",
port = 7001,
bindAddress = "192.168.10.104",
mdnsPort = 5356,
)
while (true) {
service.broadcast()
delay(2000L)
}
val service = registerService(
serviceType = "_airplay._tcp.local.",
serviceName = "AP@RTAST",
hostname = "rtakland.local.",
ipAddress = "192.168.10.104",
port = 7001,
bindAddress = "192.168.10.104",
mdnsPort = 5356,
)
while (true) {
service.broadcast()
delay(2000L) // or use cn.rtast.nmdns.sleep1(2) sleep 2 seconds
}
```
+42 -5
View File
@@ -6,19 +6,32 @@ plugins {
}
group = "cn.rtast.nmdns"
version = "0.0.1"
version = "0.0.2"
repositories {
mavenCentral()
}
kotlin {
linuxArm64()
linuxX64()
mingwX64()
macosArm64()
val nativeTargets = listOf(
linuxArm64(),
linuxX64(),
mingwX64(),
macosArm64()
)
jvm { compilerOptions { jvmTarget = JvmTarget.JVM_11 } }
nativeTargets.forEach {
it.binaries {
staticLib {
baseName = "nmdns"
}
sharedLib {
baseName = "nmdns"
}
}
}
sourceSets {
commonMain.dependencies {
implementation("io.ktor:ktor-network:3.2.3")
@@ -42,3 +55,27 @@ publishing {
}
}
}
/**
* link all targets static lib
* Apple target can only be compiled on macos
*/
tasks.register("linkStaticLibrary") {
group = "nmdns"
dependsOn(tasks.named("linkReleaseStaticLinuxArm64"))
dependsOn(tasks.named("linkReleaseStaticLinuxX64"))
dependsOn(tasks.named("linkReleaseStaticMacosArm64"))
dependsOn(tasks.named("linkReleaseStaticMingwX64"))
}
/**
* link all targrts to shared lib
* Apple target can only be compiled on macos
*/
tasks.register("linkSharedLibrary") {
group = "nmdns"
dependsOn(tasks.named("linkReleaseSharedLinuxArm64"))
dependsOn(tasks.named("linkReleaseSharedLinuxX64"))
dependsOn(tasks.named("linkReleaseSharedMacosArm64"))
dependsOn(tasks.named("linkReleaseSharedMingwX64"))
}
@@ -40,4 +40,7 @@ private val macAddresses = listOf(
"c3:c9:1c:3d:ae:dd"
)
/**
* get random mac address
*/
fun randomMacAddress(): String = macAddresses.random()
@@ -13,58 +13,62 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.io.Buffer
data class NMDNSServer(
data class NMDNSAnnouncer(
/**
* coroutine dispatcher for running server
*/
val dispatcher: CoroutineDispatcher = Dispatchers.Default,
internal val dispatcher: CoroutineDispatcher = Dispatchers.Default,
/**
* service type
* default airplay
* airplay service type: _airplay._tcp.local.
*/
val serviceType: String = "_airplay._tcp.local.",
internal val serviceType: String,
/**
* service name
* finally: $serviceName.$serviceType
*/
val serviceName: String = "APS",
internal val serviceName: String,
/**
* local hostname
*/
val hostname: String,
internal val hostname: String,
/**
* service ip, lan ip mostly
*/
val ipAddress: String,
internal val ipAddress: String,
/**
* service port
*/
val port: Int = 7004,
internal val port: Int = 7004,
/**
* mdns server port
*/
val mdnsPort: Int = 3025,
internal val mdnsPort: Int = 3025,
/**
* Txt record content
* txt record content
*/
val txtRecords: List<String> = listOf(
"deviceid=01:23:45:67:89:AB",
"model=AppleTV3,2C",
"features=0x5A7FFFF7,0x1E",
"srcvers=220.68",
"pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336"
),
internal val txtRecords: List<String>,
/**
* mdns socket server
*/
val server: BoundDatagramSocket,
internal val server: BoundDatagramSocket,
/**
* srv packet
*/
val packet: Buffer,
internal val packet: Buffer,
)
suspend fun createService(
/**
* register service
* airplay2 txt records
* listOf(
* "deviceid=${randomMacAddress()}",
* "model=AppleTV3,2C",
* "features=0x5A7FFFF7,0x1E",
* "srcvers=220.68",
* "pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336"
* )
*/
suspend fun registerService(
serviceType: String,
serviceName: String,
hostname: String,
@@ -72,25 +76,29 @@ suspend fun createService(
port: Int,
mdnsPort: Int = 9872,
bindAddress: String,
txtRecords: List<String> = listOf(
"deviceid=${randomMacAddress()}",
"model=AppleTV3,2C",
"features=0x5A7FFFF7,0x1E",
"srcvers=220.68",
"pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336"
),
): NMDNSServer {
txtRecords: List<String>,
): NMDNSAnnouncer {
val manager = SelectorManager(Dispatchers.Default)
val serverSocket = aSocket(manager).udp()
.bind(InetSocketAddress(bindAddress, mdnsPort))
return NMDNSServer(
Dispatchers.Default, serviceType, serviceName,
hostname, ipAddress, port, mdnsPort,
txtRecords, serverSocket, buildPacket(serviceType, "$serviceName.$serviceType", hostname, ipAddress, port, txtRecords)
return NMDNSAnnouncer(
Dispatchers.Default,
serviceType,
serviceName,
hostname,
ipAddress,
port,
mdnsPort,
txtRecords,
serverSocket,
buildPacket(serviceType, "$serviceName.$serviceType", hostname, ipAddress, port, txtRecords)
)
}
suspend fun NMDNSServer.broadcast() {
/**
* broadcast packet
*/
suspend fun NMDNSAnnouncer.broadcast() {
this.server.send(Datagram(packet.copy(), InetSocketAddress("224.0.0.251", 5353)))
}
@@ -7,4 +7,8 @@
package cn.rtast.nmdns
/**
* block current thread
* unit: second
*/
internal expect fun sleep1(time: Int)
+11 -3
View File
@@ -8,7 +8,8 @@
package test
import cn.rtast.nmdns.broadcast
import cn.rtast.nmdns.createService
import cn.rtast.nmdns.randomMacAddress
import cn.rtast.nmdns.registerService
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Test
@@ -40,14 +41,21 @@ class JvmTest {
// println("sent")
// delay(2000L)
// }
val service = createService(
val service = registerService(
serviceType = "_airplay._tcp.local.",
serviceName = "AP@RTAST",
hostname = "rtakland.local.",
ipAddress = "192.168.10.104",
port = 7001,
bindAddress = "192.168.10.104",
mdnsPort = 5356
mdnsPort = 5356,
txtRecords = listOf(
"deviceid=${randomMacAddress()}",
"model=AppleTV3,2C",
"features=0x5A7FFFF7,0x1E",
"srcvers=220.68",
"pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336"
)
)
while (true) {
service.broadcast()
+15 -8
View File
@@ -1,16 +1,16 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 9/29/25
*/
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 9/29/25
*/
package test
import cn.rtast.nmdns.broadcast
import cn.rtast.nmdns.createService
import cn.rtast.nmdns.randomMacAddress
import cn.rtast.nmdns.registerService
import cn.rtast.nmdns.sleep1
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.test.Test
@@ -18,14 +18,21 @@ class LinuxTest {
@Test
fun `test on linux`() {
runBlocking {
val service = createService(
val service = registerService(
serviceType = "_airplay._tcp.local.",
serviceName = "AP@RTAST",
hostname = "rtakland.local.",
ipAddress = "192.168.10.104",
port = 7001,
bindAddress = "192.168.10.104",
mdnsPort = 5356
mdnsPort = 5356,
txtRecords = listOf(
"deviceid=${randomMacAddress()}",
"model=AppleTV3,2C",
"features=0x5A7FFFF7,0x1E",
"srcvers=220.68",
"pk=f3769a660475d27b4f6040381d784645e13e21c53e6d2da6a8c3d757086fc336"
)
)
while (true) {
service.broadcast()