WebSocket:gorilla/websocket 升级、消息广播与心跳(基础版)

WebSocket 在一条 TCP 连接上做全双工通信,适合聊天、推送、协同编辑。Gin 本身不处理升级,社区最常用 gorilla/websocket 完成升级与帧读写,API 以官方仓库 README 与 pkg.go.dev 为准。

1. 升级连接

路由里用 Upgrader 把普通 HTTP 请求升级为长连接:

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        return true // 生产按来源白名单收紧,不要一律放行
    },
}

func WsHandler(c *gin.Context) {
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        return // 升级失败,由库处理了响应
    }
    defer conn.Close()
    // ... 进入读写循环
}

升级成功后 HTTP 语义不再适用,业务数据走连接帧。在 Gin 里挂载并先鉴权再升级:r.GET("/ws", AuthRequired(), WsHandler),避免匿名连接占资源。

2. 读写循环与广播

经典模型是 Hub:连接注册进 map,收到的消息统一转发:

type Hub struct {
    mu      sync.Mutex
    clients map[*websocket.Conn]bool
}

func (h *Hub) broadcast(msg []byte) {
    h.mu.Lock()
    defer h.mu.Unlock()
    for conn := range h.clients {
        conn.WriteMessage(websocket.TextMessage, msg)
    }
}

func readLoop(h *Hub, conn *websocket.Conn) {
    defer conn.Close()
    for {
        _, data, err := conn.ReadMessage()
        if err != nil {
            h.mu.Lock()
            delete(h.clients, conn)
            h.mu.Unlock()
            return
        }
        h.broadcast(data) // 示例语义:原样转给所有人
    }
}

3. 心跳:探测死连接

WebSocket 没有自动超时断连,要靠应用层心跳:服务端定期发 Ping,客户端回 Pong 就续命;读超时一到说明对端失联,ReadMessage 返回错误并回收连接:

conn.SetReadLimit(1024) // 防超大消息
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
conn.SetPongHandler(func(string) error { // 收到 Pong 重置读超时
    return conn.SetReadDeadline(time.Now().Add(60 * time.Second))
})

go func() { // 示例:每 30 秒发一次 Ping
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()
    for range ticker.C {
        if err := conn.WriteControl(websocket.PingMessage,
            nil, time.Now().Add(10*time.Second)); err != nil {
            conn.Close()
            return
        }
    }
}()

注意点

  • 建议一个连接同时只有一个 goroutine 在写;WriteMessage 并发调用不安全,广播侧要加锁或收敛到专用写协程。
  • 帧大小默认无上限,务必 SetReadLimit
  • 优雅关闭:广播 CloseMessage 并等读循环退出,别直接强杀连接。
  • 协议细节以 IETF RFC 6455 与 gorilla/websocket 官方文档为准。

小结

升级(Upgrader)→ 读写循环 → 广播 Hub → 心跳保活,是 WebSocket 服务的基本骨架。先想清楚「谁发给谁」再选 Hub/房间模型;更完整的例子可参考 gorilla 官方 chat 示例。

笔记加载中…