Golang实现WebSocket Echo服务时连接快速关闭问题
问题描述
测试如下WebSocket示例时,出现连接建立后快速关闭的错误。
原有服务端代码(Golang)
package main import ( "fmt" "log" "net/http" "golang.org/x/net/websocket" ) func Echo(ws *websocket.Conn) { var err error for { var reply string if err = websocket.Message.Receive(ws, &reply); err != nil { fmt.Println("Can't receive") break } fmt.Println("Received back from client: " + reply) msg := "Received: " + reply fmt.Println("Sending to client: " + msg) if err = websocket.Message.Send(ws, msg); err != nil { fmt.Println("Can't send") break } } } func main() { http.Handle("/", websocket.Handler(Echo)) if err := http.ListenAndServe(":1234", nil); err != nil { log.Fatal("ListenAndServe:", err) } }
原有前端客户端代码
<html> <head></head> <body> <script type="text/javascript"> var sock = null; var wsuri = "ws://localhost:1234"; window.onload = function() { console.log("onload"); sock = new WebSocket(wsuri); sock.onopen = function() { console.log("connected to " + wsuri); } sock.onclose = function(e) { console.log("connection closed (" + e.code + ")"); } sock.onmessage = function(e) { console.log("message received: " + e.data); } }; function send() { var msg = document.getElementById('message').value; sock.send(msg); }; </script> <h1>WebSocket Echo Test</h1> <form> <p> Message: <input id="message" type="text" value="Hello, world!"> </p> </form> <button onclick="send();">Send Message</button> </body> </html>
报错截图
故障原因
golang.org/x/net/websocket包长期未更新,完全不兼容现代浏览器遵循的WebSocket RFC 6455标准:
- 该包握手阶段的Origin校验逻辑不符合规范,会直接拒绝浏览器发起的合法握手请求,返回403状态码触发连接断开
- 帧解析逻辑存在兼容缺陷,无法正确识别Chrome、Firefox等主流新版浏览器发送的WebSocket数据帧
修复方案
替换为Go生态广泛使用、完全符合标准的gorilla/websocket库实现服务端,操作步骤如下:
- 安装依赖库,执行命令:
go get github.com/gorilla/websocket - 替换原有服务端代码为以下版本:
package main import ( "fmt" "log" "net/http" "github.com/gorilla/websocket" ) var upgrader = websocket.Upgrader{ // 测试阶段允许所有跨域请求,生产环境请根据实际业务校验请求来源 CheckOrigin: func(r *http.Request) bool { return true }, } func Echo(w http.ResponseWriter, r *http.Request) { ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println("WebSocket连接升级失败:", err) return } defer ws.Close() for { _, reply, err := ws.ReadMessage() if err != nil { fmt.Println("接收消息失败,连接已断开") break } replyStr := string(reply) fmt.Println("收到客户端消息: " + replyStr) msg := "Received: " + replyStr fmt.Println("向客户端发送消息: " + msg) err = ws.WriteMessage(websocket.TextMessage, []byte(msg)) if err != nil { fmt.Println("发送消息失败") break } } } func main() { http.HandleFunc("/", Echo) if err := http.ListenAndServe(":1234", nil); err != nil { log.Fatal("ListenAndServe:", err) } }
原有前端代码不需要做任何修改,重新启动服务端后即可正常建立连接、收发消息。
内容的提问来源于stack exchange,提问作者Hasan A Yousef
相关产品推荐
相关产品推荐

