英文:
Enable message compression for WebSockets in Go
问题
我有一个简单的客户端-服务器WebSocket通信,并且我想知道是否可以为WebSocket启用消息压缩。我正在使用Golang库gorila/websocket
。
有一些配置项,如EnableCompression bool
或EnableWriteCompression(bool)
方法,但它们似乎无法按预期工作,或者我可能无法弄清如何使用它们。
期望的行为是:
我期望发送一个50kb的消息,并将其压缩为10-20kb或类似的大小。但似乎EnableWriteCompression
不按预期工作,或者我没有正确使用它。
代码如下:
server.go
:
package main
import (
"fmt"
"github.com/gorilla/websocket"
"log"
"net/http"
)
var upgrader = websocket.Upgrader{}
func socketHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
conn.EnableWriteCompression(true)
if err != nil {
log.Print("Error during upgrade:", err)
return
}
defer conn.Close()
n := 0
for n <= 10 {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("Error during message reading:", err)
break
}
log.Printf("Received: %s", message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println("Error during message writing:", err)
break
}
n++
}
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Index Page")
}
func main() {
http.HandleFunc("/socket", socketHandler)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
client.go
:
// client.go
package main
import (
"log"
"os"
"os/signal"
"time"
"github.com/gorilla/websocket"
)
var done chan interface{}
var interrupt chan os.Signal
func receiveHandler(connection *websocket.Conn) {
defer close(done)
for {
_, msg, err := connection.ReadMessage()
if err != nil {
log.Println("Error in receive:", err)
return
}
log.Printf("Received: %s\n", msg)
}
}
func main() {
done = make(chan interface{}) // Channel to indicate that the receiverHandler is done
interrupt = make(chan os.Signal) // Channel to listen for interrupt signal to terminate gracefully
signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT
socketUrl := "ws://localhost:8080" + "/socket"
conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
if err != nil {
log.Fatal("Error connecting to Websocket Server:", err)
}
defer conn.Close()
go receiveHandler(conn)
// Our main loop for the client
// We send our relevant packets here
for {
select {
case <-time.After(time.Duration(1) * time.Millisecond * 1000):
conn.EnableWriteCompression(true)
conn.SetCompressionLevel(1)
err := conn.WriteMessage(websocket.TextMessage, []byte("Some message to send!"))
if err != nil {
log.Println("Error during writing to websocket:", err)
return
}
case <-interrupt:
log.Println("Received SIGINT interrupt signal. Closing all pending connections")
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println(err)
return
}
select {
case <-done:
log.Println("Exiting....")
case <-time.After(time.Duration(1) * time.Second):
log.Println("Exiting....")
}
return
}
}
}
英文:
I have simple client - server websocket communication and I want to know if it's possible to enable message compression for the websockets. I am using Golang library gorila/websocket
.
And there are configurations like EnableCompression bool
or EnableWriteCompression(bool) method
, but it does not working as expected or maybe I cannot figure out how to use it.
Expected behaviour:
I am expecting to send for example - 50kb message and to be compressed to 10-20kb or something like this. But it seems that EnableWriteCompression
is not working as expected or I am not using it in the right way.
The code:
server.go
:
package main
import (
"fmt"
"github.com/gorilla/websocket"
"log"
"net/http"
)
var upgrader = websocket.Upgrader{}
func socketHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
conn.EnableWriteCompression(true)
if err != nil {
log.Print("Error during upgrade:", err)
return
}
defer conn.Close()
n := 0
for n <= 10 {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("Error during message reading:", err)
break
}
log.Printf("Received: %s", message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println("Error during message writing:", err)
break
}
n++
}
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Index Page")
}
func main() {
http.HandleFunc("/socket", socketHandler)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
client.go
:
// client.go
package main
import (
"log"
"os"
"os/signal"
"time"
"github.com/gorilla/websocket"
)
var done chan interface{}
var interrupt chan os.Signal
func receiveHandler(connection *websocket.Conn) {
defer close(done)
for {
_, msg, err := connection.ReadMessage()
if err != nil {
log.Println("Error in receive:", err)
return
}
log.Printf("Received: %s\n", msg)
}
}
func main() {
done = make(chan interface{}) // Channel to indicate that the receiverHandler is done
interrupt = make(chan os.Signal) // Channel to listen for interrupt signal to terminate gracefully
signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT
socketUrl := "ws://localhost:8080" + "/socket"
conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
if err != nil {
log.Fatal("Error connecting to Websocket Server:", err)
}
defer conn.Close()
go receiveHandler(conn)
// Our main loop for the client
// We send our relevant packets here
for {
select {
case <-time.After(time.Duration(1) * time.Millisecond * 1000):
conn.EnableWriteCompression(true)
conn.SetCompressionLevel(1)
err := conn.WriteMessage(websocket.TextMessage, []byte("Some message to send!"))
if err != nil {
log.Println("Error during writing to websocket:", err)
return
}
case <-interrupt:
log.Println("Received SIGINT interrupt signal. Closing all pending connections")
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println(err)
return
}
select {
case <-done:
log.Println("Exiting....")
case <-time.After(time.Duration(1) * time.Second):
log.Println("Exiting....")
}
return
}
}
}
答案1
得分: 4
根据文档:
> EnableWriteCompression 启用或禁用后续文本和二进制消息的写入压缩。如果与对等方未协商压缩,则此函数无效。
您需要在 Updater
和 Dialer
级别上设置压缩,以便在连接升级期间进行协商:
// 服务器端
var upgrader = websocket.Upgrader{
EnableCompression: true,
}
// 客户端
dialer := websocket.Dialer{
Proxy: http.ProxyFromEnvironment, // 来自默认拨号器
HandshakeTimeout: 45 * time.Second, // 来自默认拨号器
EnableCompression: true,
}
...
conn, _, err := dialer.Dial(socketUrl, nil)
然而,您的示例不会显示消息是否被压缩,因为这由库处理。
您可以使用类似 Wireshark 的工具进行验证:
Sec-WebSocket-Extensions: permessage-deflate ...
以及在消息中:
.1.. .... = Per-Message Compressed: True
您可能还需要调整压缩级别以获得您期望的结果(最大值似乎为9)。
英文:
As per documentation:
> EnableWriteCompression enables and disables write compression of
subsequent text and binary messages. This function is a noop if compression was not negotiated with the peer.
You need to setup the compression on Updater
and Dialer
level, so that it can be negotiated during the connection upgrade:
// for server
var upgrader = websocket.Upgrader{
EnableCompression: true,
}
// for client
dialer := websocket.Dialer{
Proxy: http.ProxyFromEnvironment, // From default dialer
HandshakeTimeout: 45 * time.Second, // From default dialer
EnableCompression: true,
}
...
conn, _, err := dialer.Dial(socketUrl, nil)
Your example however will not show that the messages are compressed as this is handled by the library.
You can verify it using something like Wireshark:
Sec-WebSocket-Extensions: permessage-deflate ...
and on messages:
.1.. .... = Per-Message Compressed: True
You might also need to adjust the compression level to see the result you expect (max seems to be 9).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论