Kotlin Multiplatform Mobile (KMM) WebSocket 代码在 iOS 上连接两次

问题描述 投票:0回答:1

我正在开发一个 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 设备连接到服务器一次,这样主线程就不会被阻塞?

kotlin websocket coroutine kotlin-multiplatform ktor
1个回答
0
投票

我知道我参加这个聚会迟到了,但如果你仍然需要一个合适的网络套接字库,请看一下我在这里写的一个。

https://github.com/TheArchitect123/TitanSocket

© www.soinside.com 2019 - 2024. All rights reserved.