【问题标题】:Enable message compression for WebSockets in Go在 Go 中为 WebSocket 启用消息压缩
【发布时间】:2022-01-25 10:48:17
【问题描述】:

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

还有EnableCompression boolEnableWriteCompression(bool) method 之类的配置,但它没有按预期工作,或者我不知道如何使用它。

Expected behaviour:

我希望发送例如 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
        }
    }
}

【问题讨论】:

  • 您好,您确实发布了一些代码,但您没有说明您所期望的,以及没有按您期望的那样工作。你能提供更多细节吗?
  • 谢谢,我做了一个“预期行为”部分。

标签: go websocket server client


【解决方案1】:

根据文档:

EnableWriteCompression 启用和禁用写入压缩 随后的文本和二进制消息。 如果未与对等方协商压缩,则此函数为 noop。

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

// 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)

但是,您的示例不会显示消息被压缩,因为这是由库处理的。

您可以使用 Wireshark 之类的工具对其进行验证:

Sec-WebSocket-Extensions: permessage-deflate ...

关于消息:

.1.. .... = Per-Message Compressed: True

您可能还需要调整压缩级别才能看到您期望的结果(最大值似乎是 9)。

【讨论】:

    最近更新 更多