WebSocket - 关闭握手 Gorilla

4

WebSocket规范中的代码片段:

为了使用状态码(第7.4节)/code/和可选关闭原因(第7.1.6节)/reason/开始WebSocket关闭握手,终端必须发送一个Close控制帧,具体描述见第5.5.1节,其中状态码设置为/code/,关闭原因设置为/reason/。 一旦一个终端已经发送和接收到一个Close控制帧,该终端应该按照第7.1.1节定义关闭WebSocket连接

我正在尝试使用Gorilla WebSocket包执行关闭握手,并使用以下代码:

服务器:

// Create upgrader function
conn, err := upgrader.Upgrade(w, r, nil)

// If there is an error stop everything.
if err != nil {
    fmt.Println(err)
    return
}

for {
    // Read Messages
    _, _, err := conn.ReadMessage()
    // Client is programmed to send a close frame immediately...
    // When reading close frame resend close frame with same
    // reason and code
    conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(1000, "woops"))
    fmt.Println(err)
    break
}

客户端:

d := &websocket.Dialer{}

conn, _, err := d.Dial("ws://localhost:8080", nil)

if err != nil {
    fmt.Println(err)
    return
}

go func() {
    for {
        // Read Messages
        _, _, err := conn.ReadMessage()

        if c, k := err.(*websocket.CloseError); k {
            if(c.Code == 1000) {
                // Never entering since c.Code == 1005
                fmt.Println(err)
                break
            }
        }
    }
}()

conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(1000, "woops"))

for {}

服务器正常读取关闭帧并输出以下内容:

websocket: close 1000 (正常): woops

但是,客户端在发送关闭消息后似乎停止了读取。ReadMessage 继续返回错误1005。我做错了什么?
1个回答

8
服务器对关闭帧作出响应时,会使用代码
    c.WriteControl(CloseMessage, []byte{}, time.Now().Add(writeWait))

客户端将此翻译为关闭代码1005(未收到状态)。

由服务器编写的1000 oops关闭帧未被客户端应用程序看到,因为在接收第一个关闭帧后,websocket连接停止从网络读取。

当从ReadMessage返回错误时,客户端应用程序应退出循环。不需要检查特定的关闭代码。

for {
    // Read Messages
    _, _, err := conn.ReadMessage()
    if err != nil {
        break
    }
}

与问题无关,服务器应在发送关闭帧后关闭websocket连接。

与问题无关,使用select {}而不是for {}来阻塞主goroutine。前者仅阻塞goroutine,后者使用CPU时间旋转。


2
我知道这个答案很老,但是这似乎是错误的:“使用select {}而不是for {}。前者会永远阻塞。后者会一直旋转。”OP没有从通道中读取。这就是select的作用。conn.ReadMessage()已经是阻塞的了。 - Artur Sapek
3
评论是针对 OP 代码末尾使用 for {} 的。for {} 结构会浪费 CPU 时间。select {} 只会简单地阻塞程序。调用 conn.ReadMessage() 不会阻塞主 goroutine。 - Charlie Tumahai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接