Go+JS搭建WebSocket连接报错:Connection头无Upgrade令牌
WebSocket连接报错排查:gorilla/websocket协议不匹配问题
问题概述
用Go语言基于gorilla/websocket库编写后端,搭配JavaScript前端搭建WebSocket连接,运行go run main.go后服务器报错:websocket: the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' header
同时出现连接被主机端软件主动终止的错误。已确认Chrome中WebSocket请求头包含Connection: Upgrade和Upgrade: websocket字段,但问题仍未解决。
原代码片段
main.go
package main import ( "fmt" "log" "net/http" "github.com/gorilla/websocket" ) var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func homePage(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./index.html") conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println("Error in handler:", err) return } log.Println("Client connected.") for { messageType, p, err := conn.ReadMessage() if err != nil { log.Println("Fehler in ReadMessage: ", err) return } log.Println(string(p)) //echo message to client if err := conn.WriteMessage(messageType, p); err != nil { log.Println(err) return } } } func setupRoutes() { http.HandleFunc("/ws", homePage) } func main() { fmt.Println("Server gestartet") setupRoutes() log.Fatal(http.ListenAndServe(":9100", nil)) }
index.html
<!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <title>Some unimportant html </title> </head> <body> <script> let socket = new WebSocket("ws://localhost:9100/ws"); console.log("Websocket started."); socket.onOpen = () => { console.log("Client started."); } socket.onclose = (event) => { console.log("Socket closed: ", event); } socket.onError = (error) => { console.log("Socket Error: ", error); } socket.onMessage = (msg) => { console.log(msg); } </script> </body> </html>
报错信息
2022/11/20 16:38:33 http: superfluous response.WriteHeader call from github.com/gorilla/websocket.(*Upgrader).returnError (server.go:83) 2022/11/20 16:38:33 Error in handler: websocket: the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' header 2022/11/20 16:38:33 Error in handler: write tcp [::1]:9100->[::1]:63712: wsasend: Eine bestehende Verbindung wurde softwaregesteuert durch den Hostcomputer abgebrochen. 2022/11/20 16:39:06 Error in handler: write tcp [::1]:9100->[::1]:63733: wsasend: Eine bestehende Verbindung wurde softwaregesteuert durch den Hostcomputer abgebrochen. exit status 0xc000013a
注:德语报错意为“现有连接被主机端软件主动终止”。
已确认的请求头
Accept-Encoding: gzip, deflate, br Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7 Cache-Control: no-cache Connection: Upgrade Host: localhost:9100 Origin: http://localhost:9100 Pragma: no-cache Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Key: h3DWLuXsI9/GkTo+sIjyzw== Sec-WebSocket-Version: 13 Upgrade: websocket User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
解决方案及原因分析
核心问题
- 路由职责混淆:原代码中
/ws路由同时处理静态文件返回和WebSocket升级。浏览器第一次访问http://localhost:9100/ws是普通HTTP GET请求,服务器返回HTML后响应已发送,后续的WebSocket升级操作无法生效;页面加载后发起的WebSocket握手请求再次访问/ws,handler先执行http.ServeFile会尝试重复发送响应头,导致superfluous response.WriteHeader错误,最终WebSocket升级失败。 - 前端事件名错误:WebSocket API的事件名是全小写的
onopen、onmessage、onerror,原代码中大写开头的onOpen等不会被触发,导致无法看到连接成功的日志。
修改后的代码
main.go
package main import ( "fmt" "log" "net/http" "github.com/gorilla/websocket" ) var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, // 允许本地调试的跨域请求,生产环境请根据实际情况调整 CheckOrigin: func(r *http.Request) bool { return true }, } // 处理静态页面请求 func homePage(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./index.html") } // 专门处理WebSocket连接升级和消息交互 func wsHandler(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println("Error upgrading connection:", err) return } defer conn.Close() // 确保连接关闭时释放资源 log.Println("Client connected.") for { messageType, p, err := conn.ReadMessage() if err != nil { log.Println("Error reading message:", err) return } log.Println("Received message:", string(p)) // 回显消息给客户端 if err := conn.WriteMessage(messageType, p); err != nil { log.Println("Error writing message:", err) return } } } func setupRoutes() { http.HandleFunc("/", homePage) // 根路由返回静态页面 http.HandleFunc("/ws", wsHandler) // /ws路由处理WebSocket } func main() { fmt.Println("Server started on :9100") setupRoutes() log.Fatal(http.ListenAndServe(":9100", nil)) }
index.html
<!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <title>WebSocket Test</title> </head> <body> <script> let socket = new WebSocket("ws://localhost:9100/ws"); console.log("WebSocket initialized."); // 修正事件名为小写 socket.onopen = () => { console.log("Client connected to server."); // 发送测试消息验证连接 socket.send("Hello from client!"); } socket.onclose = (event) => { console.log("Socket closed:", event); } socket.onerror = (error) => { console.log("Socket error:", error); } socket.onmessage = (msg) => { console.log("Received from server:", msg.data); } </script> </body> </html>
验证步骤
- 运行修改后的
main.go:go run main.go - 访问
http://localhost:9100(而非/ws) - 打开浏览器控制台,应该能看到连接成功的日志,以及服务器回显的测试消息
内容的提问来源于stack exchange,提问作者Athshe
相关产品推荐
相关产品推荐

