You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 22:06:08