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

Gin搭配nhooyr websocket时带ticker的for循环客户端断开后无法退出问题

问题根本原因

你使用的nhooyr.io/websocket库完成WebSocket握手后,原Gin请求的c.Request.Context()不会和WebSocket连接的生命周期绑定,连接断开时这个context不会被取消,所以<-c.Request.Context().Done()永远不会触发。同时如果客户端是异常断网(没有发送正常的关闭帧),wsjson.Write可能不会立刻返回错误,就会导致循环一直运行。

解决方案

使用nhooyr.io/websocket库提供的CloseRead()方法获取和WebSocket连接生命周期绑定的context,这个context会在连接任意一端断开、出现读写错误时自动取消,我们监听这个context的Done事件即可。

修改后的代码如下:

var GetDriverLocations = func(c *gin.Context) {
    wsoptions := websocket.AcceptOptions{InsecureSkipVerify: true}
    wsconn, err := websocket.Accept(c.Writer, c.Request, &wsoptions)
    if err != nil {
        return
    }
    defer wsconn.Close(websocket.StatusInternalError, "the sky is falling")

    // 获取和WebSocket连接绑定的context,连接断开时自动取消
    ctx := wsconn.CloseRead(c.Request.Context())

    driverLocation := &models.DriverLocation{}

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
        case <-ctx.Done(): // 监听连接关闭的context
            fmt.Println("done")
            return
        }
        coords, err := driverLocation.GetDrivers()
        if err != nil {
            break
        }
        // 写操作也用绑定了连接生命周期的context
        err = wsjson.Write(ctx, wsconn, &coords)
        if err != nil {
            break
        }
    }

    fmt.Println("conn ended")
}

额外说明

  • 不需要额外判断websocket.CloseStatus,只要wsjson.Write返回错误直接退出循环即可,ctx.Done()也会覆盖所有连接断开的场景
  • 这种实现同时兼容正常关闭和异常断网的场景,不会出现残留循环的问题

内容的提问来源于stack exchange,提问作者Sami Al-Subhi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 07:12:03