You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

解决方案及原因分析

核心问题

  1. 路由职责混淆:原代码中/ws路由同时处理静态文件返回和WebSocket升级。浏览器第一次访问http://localhost:9100/ws是普通HTTP GET请求,服务器返回HTML后响应已发送,后续的WebSocket升级操作无法生效;页面加载后发起的WebSocket握手请求再次访问/ws,handler先执行http.ServeFile会尝试重复发送响应头,导致superfluous response.WriteHeader错误,最终WebSocket升级失败。
  2. 前端事件名错误: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>

验证步骤

  1. 运行修改后的main.go:go run main.go
  2. 访问http://localhost:9100(而非/ws)
  3. 打开浏览器控制台,应该能看到连接成功的日志,以及服务器回显的测试消息

内容的提问来源于stack exchange,提问作者Athshe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 04:15:18