KotlinMultiplatform移动的(KMM)WebSocket代码不适用于iOS

8i9zcol2  于 2023-08-05  发布在  Kotlin
关注(0)|答案(1)|浏览(151)

我正在做一个KotlinMultiplatform移动的(KMM)项目,我正在使用WebSockets发送和接收消息。我已经为WebSocket客户端实现了一个类WsChat,它在Android上可以正常工作,但在iOS上不行。
下面是WsChat的代码:

import com.bokutotu.dateappmobile.model.*
import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.http.*
import io.ktor.websocket.*
import kotlinx.coroutines.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

expect fun wsClient(url: String, config: HttpClientConfig<*>.() -> Unit = {}): HttpClient

class WsChat(
    private val host: String = "127.0.0.1",
    private val port: Int = 8080,
    private val chatId: Int,
    private val userId: Int,
    private val initRecieve: (String) -> Unit,
    private val onReceive: (String) -> Unit
) {
    private val client = wsClient(host)
    private var session: DefaultClientWebSocketSession? = null
    private val sessionPromise = CompletableDeferred<DefaultClientWebSocketSession?>()
    private var initialMessageReceived = false
    private val scope = CoroutineScope(Dispatchers.IO)

    init {
        scope.launch {
            initSession()
        }
    }

    private suspend fun initSession() {
        client.webSocket(
            method = HttpMethod.Get,
            host = host,
            port = port,
            path = "/message/$chatId/$userId"
        ) {
            session = this
            sessionPromise.complete(this)
            initReceivePipeline()
        }
    }

    private suspend fun initReceivePipeline() {
        val session = sessionPromise.await()
        session?.let {
            for (frame in it.incoming) {
                frameHandler(frame) { receivedText ->
                    if (!initialMessageReceived) {
                        initialMessageReceived = true
                        initRecieve(receivedText)
                    } else {
                        onReceive(receivedText)
                    }
                    println("here")
                }
            }
        }
    }

    private fun frameHandler(frame: Frame, receiveFunc: (String) -> Unit) {
        when (frame) {
            is Frame.Text -> {
                val receivedText = frame.readText()
                receiveFunc(receivedText)
            }
            else -> println("Unexpected Frame format")
        }
    }

    fun send(newMessage: NewMessageJson) {
        val json = Json.encodeToString(newMessage)
        scope.launch {
            val session = sessionPromise.await()
            session?.send(Frame.Text(json))
        }
    }

    fun close() {
        scope.cancel()
        client.close()
    }
}

字符串
这段代码在Android平台上运行良好,但在iOS上不起作用。当我运行iOS应用程序时,错误在下面。

> Task :shared:compileKotlinIosSimulatorArm64 FAILED
e: WsChat.kt:26:52 Unresolved reference: IO


我怀疑这可能与iOS中不同的线程模型有关,或者与KMM或协程中的一些细微差别有关,我可能没有意识到。有没有人知道为什么会发生这种情况,以及我如何解决它?
如果你能帮忙的话,我将不胜感激。提前感谢!

i7uq4tfw

i7uq4tfw1#

检查Dispatchers.IO的文档,您可以看到它仅在JVM平台上可用,因此在iOS目标上没有解析的引用。您应该在iOS上使用其他调度程序。顺便说一句,在使用Ktor时不需要使用特殊的调度程序,因为我相信它在内部使用自己的线程池,并且应该可以安全地从任何线程使用。
除此之外,你有点幸运,这在Android上工作。
你捕捉会话的方式是非正统的。这个client.webSocket(...) { }的重载在块到达末尾时关闭会话,所以代码只工作,因为你碰巧在监听incoming通道,直到它被关闭,而这发生在块内部,实际上只允许外部关闭会话,而永远不会到达块的末尾。(另外,您并不真正使用private var session属性,仅使用promise FYI。
相反,您可能应该使用webSocketSession函数。这一个返回会话,当你不再需要它时,你可以关闭它。这将更清楚地说明会话何时结束,以及为什么可以在send函数中使用它。
请参见此处的示例:https://github.com/joffrey-bion/krossbow/blob/main/krossbow-websocket-ktor/src/commonMain/kotlin/org/hildan/krossbow/websocket/ktor/KtorWebSocketClient.kt

相关问题