我正在开发一个 Kotlin Multiplatform Mobile (KMM) 项目,其中使用 WebSocket 发送和接收消息。我一直在遵循 ktor 客户端和服务器指南来构建一个简单的聊天应用程序。在 Android 上,一切都按预期运行,但在 iOS 上,每次 iOS 客户端连接到服务器时都会创建两个用户。这意味着 iOS 会两次接收每条消息,并被服务器两次视为两个不同的用户 ID。
客户端:https://ktor.io/docs/getting-started-ktor-client-chat.html#wire-it-together
服务器:https://ktor.io/docs/creating-web-socket-chat.html
服务器代码:
class Connection(val session: DefaultWebSocketSession) {
companion object {
val lastId = AtomicInteger(0)
}
val name = "user${lastId.getAndIncrement()}"
}
fun main(): Unit = runBlocking {
val server = embeddedServer(Netty, 13276, watchPaths = emptyList()) {
install(WebSockets) {
pingPeriod = Duration.ofSeconds(15)
timeout = Duration.ofSeconds(15)
maxFrameSize = Long.MAX_VALUE
masking = false
}
routing {
get("/") {
call.respondText("All good here in ", ContentType.Text.Plain)
}
val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet())
webSocket("/ws") {
println("Adding user!")
val thisConnection = Connection(this)
connections += thisConnection
try {
send(Frame.Text("You are connected! There are ${connections.count()} users here."))
connections.forEach {
if (it != thisConnection)
it.session.send(Frame.Text("${thisConnection.name} Connected"))
}
for (frame in incoming) {
frame as? Frame.Text ?: continue
val receivedText = frame.readText()
val textWithUsername = "[${thisConnection.name}]: $receivedText"
connections.forEach {
it.session.send(Frame.Text(textWithUsername))
}
}
} catch (e: Exception) {
println(e.localizedMessage)
} finally {
println("Removing $thisConnection!")
connections -= thisConnection
}
}
}
}
server.start(wait = true)
}
客户代码:
private fun clientMain() {
CoroutineScope(Dispatchers.Default).launch {
delay(2000)
runBlocking {
client.webSocket("ws://$localIp:13276/ws") {
send(Frame.Text("Test Message"))
for (frame in incoming)
println((frame as Frame.Text).readText())
}
}
client.close()
println("Connection closed. Goodbye!")
}
}
预期:
You are connected! There are 1 users here.
[user0]: Test Message
实际:
You are connected! There are 1 users here.
[user0]: Test Message
user1 Connected
You are connected! There are 2 users here.
[user1]: Test Message
[user1]: Test Message
客户端代码只是一个可以轻松复制问题的测试,而不是真正的聊天监听客户端。我已经测试过,在没有协程包装器并且仅使用运行阻止程序的情况下,它仅在 kmm iOS 代码上成功连接一次。然而,它在主线程上运行,并且当 websocket 处于 while 循环中像普通聊天应用程序一样监听时,该应用程序无法使用。我怀疑 Dispatcher.Default 的行为方式与 android 不同,因为它可能不是 iOS 端真正的协程。如何使用协程将 iOS 设备连接到服务器一次,这样主线程就不会被阻塞?
我知道我参加这个聚会迟到了,但如果你仍然需要一个合适的网络套接字库,请看一下我在这里写的一个。