Golang中Websocket连接快速关闭问题排查

Golang中Websocket连接快速关闭问题排查 我正在尝试这个WebSocket示例:

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>

但是遇到了以下错误: enter image description here


更多关于Golang中Websocket连接快速关闭问题排查的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

我使用Firefox的WebSocket扩展测试了你的服务器代码,它运行正常,所以请检查你客户端的代码。

更多关于Golang中Websocket连接快速关闭问题排查的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Yamil_Bracho:

我测试了你的服务器代码

你检查的是哪一个,是原问题中使用 "golang.org/x/net/websocket" 的那个,还是使用 "nhooyr.io/websocket" 的那个?

日志显示连接失败。

你检查过 Echo 函数是否被调用了吗?

虽然不是直接相关,但该库的文档建议使用其他方案:

与一些替代的、维护更活跃的 WebSocket 包相比,本包目前缺少一些功能:

websocket package - github.com/gorilla/websocket - Go Packages websocket package - nhooyr.io/websocket - Go Packages

问题在于golang.org/x/net/websocket包在处理WebSocket协议时存在兼容性问题。客户端发送的WebSocket握手请求包含Sec-WebSocket-Version: 13,但该包默认使用较旧的协议版本。

以下是修复后的服务端代码:

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:", err)
			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:", err)
			break
		}
	}
}

func main() {
	// 设置WebSocket处理器,指定协议版本
	http.Handle("/", websocket.Handler(Echo))
	
	// 或者使用更明确的配置
	http.Handle("/ws", websocket.Handler(Echo))
	
	fmt.Println("Server starting on :1234...")
	if err := http.ListenAndServe(":1234", nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

或者使用更现代的gorilla/websocket包,它支持最新的WebSocket协议:

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 Echo(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 {
		messageType, message, err := conn.ReadMessage()
		if err != nil {
			log.Println("Read error:", err)
			break
		}

		fmt.Printf("Received: %s\n", message)

		response := []byte(fmt.Sprintf("Received: %s", message))
		if err := conn.WriteMessage(messageType, response); err != nil {
			log.Println("Write error:", err)
			break
		}
	}
}

func main() {
	http.HandleFunc("/", Echo)
	
	fmt.Println("Server starting on :1234...")
	if err := http.ListenAndServe(":1234", nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

客户端HTML代码也需要相应调整:

<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);
            }
            
            sock.onerror = function(e) {
                console.log("WebSocket error: ", e);
            }
        };

        function send() {
            var msg = document.getElementById('message').value;
            if (sock.readyState === WebSocket.OPEN) {
                sock.send(msg);
            } else {
                console.log("WebSocket is not connected");
            }
        };
    </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>

使用gorilla/websocket包时,需要先安装:

go get github.com/gorilla/websocket

主要问题:

  1. golang.org/x/net/websocket包使用旧的WebSocket协议草案
  2. 现代浏览器默认使用RFC 6455协议版本
  3. 协议版本不匹配导致握手失败

修复方案:

  1. 使用gorilla/websocket包(推荐)
  2. 或者确保客户端和服务端使用兼容的协议版本
  3. 检查WebSocket端点路径是否正确
回到顶部