英文:
WebSocket - Closing Handshake Gorilla
问题
从WebSocket RFC中的代码片段中可以看到:
> 要使用状态码(第7.4节)/code/和可选的关闭原因(第7.1.6节)/reason/启动WebSocket关闭握手,端点必须发送一个Close控制帧,如第5.5.1节所述,其状态码设置为/code/,关闭原因设置为/reason/。一旦端点发送和接收到Close控制帧,该端点应根据第7.1.1节中定义的规定_关闭WebSocket连接_。
我正在尝试使用Gorilla WebSocket包进行关闭握手,以下是使用以下代码的服务器端:
服务器端:
// 创建升级器函数
conn, err := upgrader.Upgrade(w, r, nil)
// 如果有错误,停止一切操作。
if err != nil {
fmt.Println(err)
return
}
for {
// 读取消息
_, _, err := conn.ReadMessage()
// 客户端编程立即发送关闭帧...
// 在读取关闭帧时,重新发送具有相同原因和代码的关闭帧
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 {
// 读取消息
_, _, err := conn.ReadMessage()
if c, k := err.(*websocket.CloseError); k {
if(c.Code == 1000) {
// 由于c.Code == 1005,永远不会进入此处
fmt.Println(err)
break
}
}
}
}()
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(1000, "woops"))
for {}
服务器正在按预期读取关闭帧,输出如下:
> websocket: close 1000 (normal): woops
然而,客户端似乎在发送关闭消息后停止读取。ReadMessage
继续返回错误1005。我做错了什么?
英文:
Snippet from WebSocket RFC:
> To Start the WebSocket Closing Handshake with a status code (Section 7.4) /code/ and an optional close reason (Section 7.1.6) /reason/, an endpoint MUST send a Close control frame, as described in Section 5.5.1, whose status code is set to /code/ and whose close reason is set to /reason/. Once an endpoint has both sent and received a Close control frame, that endpoint SHOULD Close the WebSocket Connection as defined in Section 7.1.1.
I am trying to do the Close Handshake using Gorilla WebSocket package with the following code:
Server:
// 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
}
Client:
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 {}
The server is reading the Close Frame as expected outputting the following:
> websocket: close 1000 (normal): woops
However the client is like its stopping to read once it sends a close message. The ReadMessage
continue to return error 1005. What am I doing wrong?
答案1
得分: 8
服务器使用以下代码对关闭帧进行响应:
c.WriteControl(CloseMessage, []byte{}, time.Now().Add(writeWait))
客户端将其翻译为关闭代码1005(未收到状态)。
由于在接收到第一个关闭帧后,WebSocket连接停止从网络读取,因此客户端应用程序无法看到服务器写入的1000 oops关闭帧。
当从ReadMessage返回错误时,客户端应用程序应退出循环。无需检查特定的关闭代码。
for {
// 读取消息
_, _, err := conn.ReadMessage()
if err != nil {
break
}
}
与问题无关,服务器应在发送关闭帧后关闭WebSocket连接。
与问题无关,使用select {}
而不是for {}
来阻塞主goroutine。前者只是阻塞goroutine,而后者会使用CPU时间。
英文:
The server responds to a close frame with the code:
c.WriteControl(CloseMessage, []byte{}, time.Now().Add(writeWait))
This is translated to close code 1005 (no status received) by the client.
The 1000 oops close frame written by the server is not seen by the client application because the websocket connection stops reading from network after receiving the first close frame.
The client application should exit the loop when an error is returned from ReadMessage. There's no need to check for specific close codes.
for {
// Read Messages
_, _, err := conn.ReadMessage()
if err != nil {
break
}
}
Unrelated to the issue in the question, the server application should close the websocket connection after sending the close frame.
Also unrelated to the issue in the question, use select {}
instead of for {}
to block the main goroutine. The former simply blocks the goroutine. The latter spins using CPU time.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论