Go标准库不支持WebSocket,需用gorilla/websocket;服务端用Upgrader升级连接后读写消息,客户端需手动拨号并处理重连与超时。

Go标准库不支持WebSocket,必须用第三方包
Go语言原生net/http包只提供HTTP协议支持,没有内置WebSocket实现。直接调用http.ResponseWriter的Write方法或试图升级连接会失败,因为缺少WebSocket握手、帧解析和心跳等关键逻辑。
主流选择是gorilla/websocket——它稳定、文档清晰、被大量生产项目验证。别用已归档的golang.org/x/net/websocket,也不建议从零手写WebSocket帧处理。
服务端:用gorilla/websocket建立连接并读写消息
核心是websocket.Upgrader升级HTTP请求,再用*websocket.Conn收发数据。注意升级过程必须在HTTP handler中完成,且不能有任何前置Write或Header修改(否则触发http: multiple response.WriteHeader calls错误)。
-
Upgrader.CheckOrigin默认拒绝跨域请求,开发时可临时设为func(r *http.Request) bool { return true },生产环境应校验Origin头 - 读取消息用
conn.ReadMessage(),返回(int, []byte, error),第一个值是消息类型(websocket.TextMessage或websocket.BinaryMessage) - 发送消息用
conn.WriteMessage(),第二个参数必须是[]byte;若传字符串,需先转[]byte("hello") - 连接应显式关闭:
conn.Close(),否则可能泄漏goroutine和文件描述符
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Upgrade error:", err)
return
}
defer conn.Close()
for {
_, msg, err := conn.ReadMessage()
if err != nil {
log.Println("Read error:", err)
break
}
if err := conn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("Write error:", err)
break
}
}
}
func main() {
http.HandleFunc("/ws", wsHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
客户端:用websocket.Dial连接并处理断线重连
浏览器端用new WebSocket("ws://localhost:8080/ws")即可,但Go客户端需主动拨号。常见陷阱是忽略TLS配置(wss://需传&tls.Config{InsecureSkipVerify: true}仅限测试)、未设置读写超时导致goroutine卡死。
立即学习“go语言免费学习笔记(深入)”;
-
websocket.DefaultDialer默认无超时,务必设置TLSClientConfig和Proxy(如需代理) - 读写操作可能阻塞,建议用
conn.SetReadDeadline()和conn.SetWriteDeadline()控制超时 - 断线后不能复用旧
*websocket.Conn,必须重新Dial;重连逻辑需自己实现(例如指数退避) - 发送JSON数据时,先
json.Marshal再WriteMessage;接收时先ReadMessage再json.Unmarshal
消息粘包与并发安全:每个连接应独占一个goroutine
websocket.Conn本身不是并发安全的——多个goroutine同时调用WriteMessage会panic(concurrent write to websocket connection)。但ReadMessage可以并发调用,只要写操作被串行化。
- 典型模式:一个goroutine循环
ReadMessage,另一个goroutine负责WriteMessage,通过chan []byte通信 - 不要在HTTP handler里直接处理业务逻辑,应把
conn交给worker goroutine,并用defer conn.Close()确保清理 - 大消息(>1MB)可能触发
websocket: close sent,需调大Upgrader.ReadBufferSize和WriteBufferSize
真正难的是状态同步和连接生命周期管理,比如用户登录态如何绑定到Conn、如何广播消息给房间内所有人——这些不在WebSocket协议层,得自己设计。









