Ktor WebSocket Client启动1分钟左右无故断开 报ClosedReceiveChannelException
问题根因
- 你使用的CIO引擎默认配置的空闲连接超时为60秒,只要连接连续60秒没有任何帧收发,客户端就会主动断开连接,此时
incoming通道被关闭,调用receive()就会抛出ClosedReceiveChannelException,这就是你不管对接哪个服务端都1分钟左右必现问题的核心原因。 - 你当前的异常处理逻辑存在错误:
ClosedReceiveChannelException属于WebSocket通道正常关闭时必然抛出的预期异常,不属于未知异常,你将它当成错误打印日志还直接关闭整个HttpClient的处理方式不合理。 - 部分服务端也会主动断开长时间没有心跳交互的连接,就算修改客户端超时配置,也可能因为缺少心跳触发相同问题。
修复方案
1. 修改CIO引擎超时配置
关闭或拉长空闲超时时间:
val client = HttpClient(CIO) { install(WebSockets) engine { // 空闲超时设为0代表永不超时,可按需调整为你需要的时长,单位为毫秒 idleTimeout = 0 requestTimeout = 0 } }
2. 优化异常处理逻辑
单独处理正常关闭的情况,不要随意关闭整个HttpClient(除非你确定后续不会复用该实例):
// Coroutine Scope client.webSocket(host = "ws.ifelse.io") { try { while (true) { val rawPayload = incoming.receive() as? Frame.Text ?: continue println(rawPayload.readText()) } } catch (e: ClosedReceiveChannelException) { // 此处为连接正常关闭的预期通知,按需添加重连逻辑即可 println("WebSocket连接已断开,准备重连") } catch (e: Exception) { log.error("WebSocket处理发生未知异常:\n${e.stackTraceToString()}") } }
3. 可选:添加心跳逻辑
定期发送Ping帧避免服务端主动断开连接:
client.webSocket(host = "ws.ifelse.io") { // 启动独立协程发送心跳 launch { while (isActive) { delay(30000) // 每30秒发送一次心跳 send(Frame.Ping()) } } try { while (true) { val rawPayload = incoming.receive() as? Frame.Text ?: continue println(rawPayload.readText()) } } catch (e: ClosedReceiveChannelException) { println("WebSocket连接已断开,准备重连") } catch (e: Exception) { log.error("WebSocket处理发生未知异常:\n${e.stackTraceToString()}") } }
内容的提问来源于stack exchange,提问作者Maxim
相关产品推荐
相关产品推荐

