KMM项目iOS端WebSocket重复连接:如何实现非阻塞单次连接?
解决KMM iOS端WebSocket双重连接问题
我在开发Kotlin Multiplatform Mobile(KMM)项目,用WebSocket实现聊天功能,Android端运行正常,但iOS端每次连接服务器会创建两个用户,导致接收每条消息两次,服务器也会将其识别为两个不同的用户ID。测试发现直接用runBlocking能实现单次连接,但会阻塞主线程,无法正常使用应用,怀疑iOS端Dispatchers.Default行为与Android不同,需要实现非阻塞的单次连接方案。
问题根源
你的客户端代码在CoroutineScope(Dispatchers.Default).launch内部嵌套了runBlocking,这在Kotlin/Native的iOS协程模型中会触发意外的重复执行,导致两次WebSocket连接请求。同时runBlocking会阻塞线程,违背了非阻塞的设计初衷。
解决方案
移除嵌套的runBlocking,改用适配网络场景的协程调度器,同时做好生命周期管理:
修正后的客户端代码:
private fun clientMain() { // 建议根据平台绑定合适的协程作用域,比如iOS用MainScope,Android用viewModelScope val scope = CoroutineScope(Dispatchers.IO) scope.launch { delay(2000) try { client.webSocket("ws://$localIp:13276/ws") { send(Frame.Text("Test Message")) for (frame in incoming) { val message = (frame as Frame.Text).readText() println(message) // 若需更新UI,切换到Main调度器(iOS UI操作必须在主线程执行) withContext(Dispatchers.Main) { // 此处添加UI更新逻辑 } } } } catch (e: Exception) { println("WebSocket连接异常: ${e.localizedMessage}") } finally { client.close() println("连接已关闭") } } }
关键说明
- 移除
runBlocking:webSocket本身是挂起函数,可直接在协程内调用,无需阻塞线程。 - 选择适配的调度器:网络IO操作推荐使用
Dispatchers.IO,Dispatchers.Default更适合CPU密集型任务。 - UI线程切换:iOS的UI操作必须在主线程执行,收到消息后若要更新UI,需通过
withContext(Dispatchers.Main)切换调度器。 - 生命周期管理:避免使用全局协程作用域,建议绑定到页面/组件的生命周期(比如iOS中在视图控制器初始化时创建
MainScope,销毁时调用cancel())。
内容的提问来源于stack exchange,提问作者Adrian S
相关产品推荐
相关产品推荐

