我正在尝试使用 Ktor 和协程接收 UDP 数据报。我创建了一个简单的 android kotlin 应用程序,并设法使用阻塞 java.nio.DatagramChannel 接收数据报,但不使用 Ktor 的套接字。类似的暂停方法也适用于桌面 Kotlin 应用程序。该应用程序在 Android Studio 模拟器(API 30 设备)和 Android 11 手机上进行了测试。
安卓应用程序
添加到AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
添加到 build.gradle 中的依赖项:
implementation 'io.ktor:ktor-network:2.2.4'
MainViewModel.kt:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.ktor.network.selector.*
import io.ktor.network.sockets.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
import java.nio.channels.DatagramChannel
import java.util.concurrent.Executors
class MainViewModel: ViewModel() {
private val receiverService = Executors.newSingleThreadExecutor()
private var messageChannel = DatagramChannel.open()
private val selector = SelectorManager(Dispatchers.IO)
private val portBlocking = 15100
private val portSuspend = 15101
init {
addCloseable(selector)
addCloseable(messageChannel)
receiveBlocking()
receiveSuspend()
}
private fun receiveBlocking() {
val bindAddress = java.net.InetSocketAddress(portBlocking)
messageChannel.bind(bindAddress)
println("--- Blocking receive at ${messageChannel.localAddress} UDP ---")
receiverService.submit {
val buf = ByteBuffer.allocate(1000)
messageChannel.receive(buf)
println("--- Received blocking $buf ---")
}
}
private fun receiveSuspend() = viewModelScope.launch {
val localAddress = InetSocketAddress("127.0.0.1", portSuspend)
val inSocket = aSocket(selector).udp().bind(localAddress) { reuseAddress = true }
println("--- Suspend receive at ${inSocket.localAddress} UDP ---")
val datagram = inSocket.receive()
println("--- Received suspend ${datagram.packet.readText()} ---")
}
}
工作桌面应用程序
import io.ktor.network.selector.*
import io.ktor.network.sockets.*
import kotlinx.coroutines.*
const val port = 9000
fun main(args: Array<String>) = runBlocking {
val selectorManager = SelectorManager(Dispatchers.IO)
receiveSuspend(selectorManager)
}
private suspend fun receiveSuspend(selector: SelectorManager) {
val localAddress = InetSocketAddress("127.0.0.1", port)
val inSocket = aSocket(selector).udp().bind(localAddress) { reuseAddress = true }
println("--- Suspend receive at ${inSocket.localAddress} UDP ---")
val datagram = inSocket.receive()
println("--- Received ${datagram.packet.readText()} ---")
}
我遇到了同样的问题。您实际上需要指定发送者的IP地址。如果您不知道,您可以简单地使用“0.0.0.0”(广播接收)。
val inSocket = aSocket(selector).udp().bind(InetSocketAddress("0.0.0.0", port))