websocket连接建立后立刻断开,因responsewriter被复用;多用户广播需中心协程串行化;发消息须检查writemessage错误;nginx需透传upgrade和connection头。

WebSocket 连接建立后立刻断开?检查 http.ResponseWriter 是否被复用
Go 的 http.ServeHTTP 处理函数里,http.ResponseWriter 是一次性的。如果在升级 WebSocket 前往它写了响应头、状态码或任何内容(比如调用了 w.WriteHeader() 或 fmt.Fprint(w, ...)),gorilla/websocket 的 Upgrader.Upgrade() 会直接 panic 并返回 "websocket: response already committed" 错误。
- 确保
Upgrade()是 handler 中第一个可能产生 HTTP 输出的操作 - 不要在
Upgrade()前调用w.Header().Set()、w.WriteHeader()或任何写入响应体的函数 - 如果需要鉴权,放在
Upgrade()之前做纯逻辑判断,别碰w - 示例错误写法:
w.WriteHeader(200); conn, err := upgrader.Upgrade(w, r, nil)→ 必挂
多个用户怎么共享广播通道?用 map[*websocket.Conn]bool + 互斥锁不顶用
单纯用 map 存连接、每次广播遍历它,看似简单,但实际运行中极易触发 concurrent map iteration and map write panic —— 因为 WebSocket 读/写协程和广播协程同时访问同一 map,且 Go 的 map 不是并发安全的。
- 别用
sync.RWMutex包一层就以为 safe:读操作仍可能和 map 扩容冲突,Go 官方明确不保证“只读”迭代的安全性 - 正确做法是用
sync.Map存连接引用,或更推荐:把连接注册/注销、广播动作全部串行化到一个 goroutine(即“中心广播协程”) - 每个连接的读协程只负责接收消息并发送到中心 channel;广播协程从该 channel 收消息,再遍历当前活跃连接列表发出去
- 这样避免了 map 并发读写,也规避了遍历时连接突然关闭导致
write tcp: use of closed network connection
用户发消息后自己收不到回显?conn.WriteMessage() 没做连接有效性检查
WebSocket 连接可能因网络抖动、客户端关闭、心跳超时等随时断开,但 *websocket.Conn 对象本身不会自动置 nil。直接对已关闭连接调用 WriteMessage() 会返回 "use of closed network connection",但若没检查 error,消息就静默丢失,用户感觉“发了没反应”。
- 每次
WriteMessage()后必须检查 error,if err != nil { deleteFromClients(); return } - 不要依赖
conn.Close()调用时机:它只是发 FIN 包,底层 TCP 连接可能还没真正断开,写操作仍可能失败 - 广播时对每个连接单独 try-write,并忽略单个失败(避免一个坏连接卡住整个广播)
- 示例关键检查:
if err := conn.WriteMessage(msgType, data); err != nil { log.Printf("write error: %v", err); conn.Close(); }
为什么本地测试正常,部署到 Nginx 后 WebSocket 400?漏配 Upgrade 和 Connection header
Nginx 默认不透传 WebSocket 升级请求所需的两个关键 header:Upgrade: websocket 和 Connection: upgrade。没它们,gorilla/websocket 在 Upgrade() 阶段直接拒绝,返回 400 Bad Request。
立即学习“go语言免费学习笔记(深入)”;
- 反向代理配置里必须显式设置:
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade"; -
$http_upgrade是 Nginx 内置变量,会取原始请求的Upgradeheader 值(通常是websocket) - 注意引号:第二行的
"upgrade"必须加双引号,否则 Nginx 会把它当变量名解析 - 如果用 Caddy 或 Traefik,同样要查对应文档确认 WebSocket 升级 header 是否透传










