在Go语言中为WebSockets启用消息压缩。

huangapple go评论66阅读模式
英文:

Enable message compression for WebSockets in Go

问题

我有一个简单的客户端-服务器WebSocket通信,并且我想知道是否可以为WebSocket启用消息压缩。我正在使用Golang库gorila/websocket

有一些配置项,如EnableCompression boolEnableWriteCompression(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 (
&quot;fmt&quot;
&quot;github.com/gorilla/websocket&quot;
&quot;log&quot;
&quot;net/http&quot;
)
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(&quot;Error during upgrade:&quot;, err)
return
}
defer conn.Close()
n := 0
for n &lt;= 10 {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println(&quot;Error during message reading:&quot;, err)
break
}
log.Printf(&quot;Received: %s&quot;, message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println(&quot;Error during message writing:&quot;, err)
break
}
n++
}
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, &quot;Index Page&quot;)
}
func main() {
http.HandleFunc(&quot;/socket&quot;, socketHandler)
http.HandleFunc(&quot;/&quot;, home)
log.Fatal(http.ListenAndServe(&quot;localhost:8080&quot;, nil))
}

client.go:

// client.go
package main
import (
&quot;log&quot;
&quot;os&quot;
&quot;os/signal&quot;
&quot;time&quot;
&quot;github.com/gorilla/websocket&quot;
)
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(&quot;Error in receive:&quot;, err)
return
}
log.Printf(&quot;Received: %s\n&quot;, 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 := &quot;ws://localhost:8080&quot; + &quot;/socket&quot;
conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
if err != nil {
log.Fatal(&quot;Error connecting to Websocket Server:&quot;, err)
}
defer conn.Close()
go receiveHandler(conn)
// Our main loop for the client
// We send our relevant packets here
for {
select {
case &lt;-time.After(time.Duration(1) * time.Millisecond * 1000):
conn.EnableWriteCompression(true)
conn.SetCompressionLevel(1)
err := conn.WriteMessage(websocket.TextMessage, []byte(&quot;Some message to send!&quot;))
if err != nil {
log.Println(&quot;Error during writing to websocket:&quot;, err)
return
}
case &lt;-interrupt:
log.Println(&quot;Received SIGINT interrupt signal. Closing all pending connections&quot;)
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, &quot;&quot;))
if err != nil {
log.Println(err)
return
}
select {
case &lt;-done:
log.Println(&quot;Exiting....&quot;)
case &lt;-time.After(time.Duration(1) * time.Second):
log.Println(&quot;Exiting....&quot;)
}
return
}
}
}

答案1

得分: 4

根据文档:
> EnableWriteCompression 启用或禁用后续文本和二进制消息的写入压缩。如果与对等方未协商压缩,则此函数无效。

您需要在 UpdaterDialer 级别上设置压缩,以便在连接升级期间进行协商:

// 服务器端
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).

huangapple
  • 本文由 发表于 2022年1月25日 18:48:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/70847343.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定