当前位置: 首页 > news >正文

Go Web 编程快速入门 11 - WebSocket实时通信:实时消息推送和双向通信

WebSocket 提供了浏览器与服务端的全双工通信能力,适合实时消息、在线协作、游戏等场景。本章延续第 04.1 章的风格,从最小可运行示例到工程化封装与房间管理,帮助你快速构建稳定的实时系统。

1 WebSocket基础与握手

1.1 升级器最小封装

package wsimport ("net/http""time""github.com/gorilla/websocket"
)type Config struct {ReadTimeout    time.DurationWriteTimeout   time.DurationPingInterval   time.DurationMaxMessageSize int64CheckOrigin    bool
}func DefaultConfig() Config {return Config{ReadTimeout:    60 * time.Second,WriteTimeout:   10 * time.Second,PingInterval:   30 * time.Second,MaxMessageSize: 1 << 20, // 1MBCheckOrigin:    false,}
}type Upgrader struct {u      websocket.Upgraderconfig Config
}func NewUpgrader(cfg Config) *Upgrader {if cfg.ReadTimeout == 0 { cfg = DefaultConfig() }return &Upgrader{config: cfg,u: websocket.Upgrader{ReadBufferSize:  4096,WriteBufferSize: 4096,CheckOrigin: func(r *http.Request) bool { return !cfg.CheckOrigin || r.Header.Get("Origin") != "" },},}
}func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {conn, err := u.u.Upgrade(w, r, nil)if err != nil { return nil, err }conn.SetReadLimit(u.config.MaxMessageSize)conn.SetReadDeadline(time.Now().Add(u.config.ReadTimeout))conn.SetPongHandler(func(string) error {conn.SetReadDeadline(time.Now().Add(u.config.ReadTimeout))return nil})return conn, nil
}

1.2 连接读写泵(最小实现)

type Conn struct {c       *websocket.Conncfg     Configsend    chan []byteclosing chan struct{}
}func NewConn(c *websocket.Conn, cfg Config) *Conn {conn := &Conn{c: c, cfg: cfg, send: make(chan []byte, 256), closing: make(chan struct{})}go conn.readPump(); go conn.writePump()return conn
}func (c *Conn) readPump() {defer c.Close()for {select {case <-c.closing:returndefault:mt, msg, err := c.c.ReadMessage()if err != nil { return }if mt == websocket.TextMessage {// 这里可以接入路由或房间广播}}}
}func (c *Conn) writePump() {ticker := time.NewTicker(c.cfg.PingInterval)defer ticker.Stop()for {select {case msg := <-c.send:c.c.SetWriteDeadline(time.Now().Add(c.cfg.WriteTimeout))if err := c.c.WriteMessage(websocket.TextMessage, msg); err != nil { return }case <-ticker.C:c.c.SetWriteDeadline(time.Now().Add(c.cfg.WriteTimeout))if err := c.c.WriteMessage(websocket.PingMessage, nil); err != nil { return }case <-c.closing:return}}
}func (c *Conn) Close() { close(c.closing); _ = c.c.Close() }
func (c *Conn) Send(data []byte) { select { case c.send <- data: default: } }

2 连接管理与房间

2.1 连接池与房间模型

type Hub struct {conns  map[string]*Conn           // sessionID -> Connrooms  map[string]map[string]bool // roomID -> sessionID set
}func NewHub() *Hub {return &Hub{conns: make(map[string]*Conn), rooms: make(map[string]map[string]bool)}
}func (h *Hub) Add(sessionID string, c *Conn) { h.conns[sessionID] = c }
func (h *Hub) Remove(sessionID string)        { delete(h.conns, sessionID) }func (h *Hub) Join(roomID, sessionID string) {if h.rooms[roomID] == nil { h.rooms[roomID] = make(map[string]bool) }h.rooms[roomID][sessionID] = true
}func (h *Hub) Leave(roomID, sessionID string) { if h.rooms[roomID] != nil { delete(h.rooms[roomID], sessionID) } }func (h *Hub) Broadcast(roomID string, data []byte) {for sid := range h.rooms[roomID] {if c := h.conns[sid]; c != nil { c.Send(data) }}
}

2.2 路由与消息分发

你可以在 readPump 中解析消息类型(文本/JSON),根据房间或目标用户进行分发。为保证健壮性,建议:

  • 控制消息大小与速率(令牌桶)。
  • 统一格式化错误与系统消息。
  • 对外暴露“加入/离开房间、私聊、群发”三类基础命令。

3 心跳与断线重连

  • 服务端定期发送 Ping,客户端回复 Pong
  • 超过 PongTimeout 未响应则关闭连接并清理资源。
  • 客户端应在断线后自动重连,并回放必要的状态(如重新加入房间)。

4 完整示例:聊天室服务

package mainimport ("encoding/json""fmt""log""net/http""time""github.com/gorilla/websocket"
)var upgrader = ws.NewUpgrader(ws.DefaultConfig())
var hub = NewHub()func main() {http.HandleFunc("/ws", handleWS)http.HandleFunc("/rooms/broadcast", httpBroadcast)log.Println("WebSocket 服务: http://localhost:8080/ws")log.Fatal(http.ListenAndServe(":8080", nil))
}func handleWS(w http.ResponseWriter, r *http.Request) {conn, err := upgrader.Upgrade(w, r)if err != nil { http.Error(w, err.Error(), http.StatusBadRequest); return }c := NewConn(conn, ws.DefaultConfig())sessionID := fmt.Sprintf("s_%d", time.Now().UnixNano())hub.Add(sessionID, c)hub.Join("lobby", sessionID) // 默认加入大厅c.Send([]byte("欢迎加入 lobby"))
}func httpBroadcast(w http.ResponseWriter, r *http.Request) {type req struct{ Room string; Content string }var body reqif err := json.NewDecoder(r.Body).Decode(&body); err != nil { http.Error(w, "请求体错误", 400); return }hub.Broadcast(body.Room, []byte(body.Content))_ = json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}

通过上述最小实现,你已具备搭建聊天室的核心能力。后续可以加入鉴权、消息持久化、在线状态、服务端水平扩展(如使用 Redis/PubSub 做跨实例广播)等增强特性。

http://www.dtcms.com/a/540064.html

相关文章:

  • 科研数据可视化工具:助力学术成果清晰呈现
  • 基于GIS的智慧畜牧数据可视化监控平台
  • 热力图可视化为何被广泛应用?| 图扑数字孪生
  • 个人简历网页html代码做网站优化最快的方式
  • Jenkins 已成过去式!新兴替代工具GitHub Actions即将崛起
  • 数组-环形数组【arr2】
  • 打开AI黑箱:SHAP让医疗AI决策更清晰的编程路径
  • 营销型商务网站wordpress html5 主题
  • 知识掘金者:API+Dify工作流,开启「深度思考」的搜索革命
  • 《道德经》第三十八章
  • 企业网站管理系统湖南岚鸿搜狗网站入口
  • 汕头网站推广制作怎么做济南源聚网络公司
  • webrtc代码走读(十)-QOS-Sender Side BWE原理
  • 102-Spring AI Alibaba RAG Pgvector 示例
  • 【刷机分享】解决K20Pro刷入PixelOS后“网络连接”受限问题(附详细ADB命令)
  • Rust 语言入门基础教程:从环境搭建到 Cargo 工具链
  • 【Linux】HTTPS协议
  • node.js 和npm 搭建项目基本流程
  • 【STM32】PWR电源控制
  • 做网页局域网站点配置wordpress仿简书主题
  • 《Linux篇》进程控制——进程创建(写时拷贝)、进程终止(退出码,exit,_exit)
  • 【MATLAB 数据分析学习指南】
  • Android PDF 操作 - AndroidPdfViewer 显示 PDF 异常清单(数据为 null、数据为空、PDF 文件损坏、非 PDF 文件)
  • 界面控件DevExpress WPF v25.2预览 - 模板工具包全新升级
  • 【音视频】H264中的SPS和PPS
  • ThinkPHP6 集成TCP长连接 GatewayWorker
  • TMap的查询
  • SpringCloud--Sleuth 解析
  • 【C++:继承和多态】多态加餐:面试常考——多态的常见问题11问
  • 零基础新手小白快速了解掌握服务集群与自动化运维(十五)Redis模块-哨兵集群