Golang 官方認可的 websocket 庫 - gorilla-websocket

推薦理由

Golang 官方標準庫實現的 websocket 在功能上有些欠缺,本次介紹的 gorilla/websocket 庫,是 Gorilla 出品的速度快、質量高,並且被廣泛使用的 websocket 庫,很好的彌補了標準庫功能上的欠缺。另外 Gorilla Web toolkit 包含多個實用的 HTTP 應用相關的工具庫,感興趣可以到官網主頁 https://www.gorillatoolkit.org 自取。

功能介紹

gorilla/websocket 庫是 RFC 6455 定義的 websocket 協議的一種實現,在數據收發方面,提供 Data Messages、Control Messages 兩類 message 粒度的讀寫 API;性能方面,提供 Buffers 和 Compression 的相關配置選項;安全方面,可通過 CheckOrigin 來控制是否支持跨域。

gorilla/websocket 庫和官方實現的對比

摘自 gorilla GitHub 主頁

ZYV2HV

使用指南

安裝

go get github.com/gorilla/websocket

基礎示例

下面以一個簡單的 echo 來說明 gorilla/websocket 庫基本使用。

client 代碼:

package main

import (
    "flag"
    "log"
    "net/url"
    "os"
    "os/signal"
    "time"

    "github.com/gorilla/websocket"
)

var addr = flag.String("addr", "localhost:8080", "http service address")

func main() {
    flag.Parse()
    log.SetFlags(0)

    // 用來接收命令行的終止信號
    interrupt := make(chan os.Signal, 1)
    signal.Notify(interrupt, os.Interrupt)

    // 和服務端建立連接
    u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
    log.Printf("connecting to %s", u.String())

    c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer c.Close()

    done := make(chan struct{})

    go func() {
        defer close(done)
        for {
            // 從接收服務端message
            _, message, err := c.ReadMessage()
            if err != nil {
                log.Println("read:", err)
                return
            }
            log.Printf("recv: %s", message)
        }
    }()

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-done:
            return
        case t := <-ticker.C:
            // 向服務端發送message
            err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
            if err != nil {
                log.Println("write:", err)
                return
            }
        case <-interrupt:
            log.Println("interrupt")

            // 收到命令行終止信號,通過發送close message關閉連接。
            err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
            if err != nil {
                log.Println("write close:", err)
                return
            }
            // 收到接收協程完成的信號或者超時,退出
            select {
            case <-done:
            case <-time.After(time.Second):
            }
            return
        }
    }
}

server 代碼:

package main

import (
    "flag"
    "html/template"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

var addr = flag.String("addr", "localhost:8080", "http service address")

var upgrader = websocket.Upgrader{}

func echo(w http.ResponseWriter, r *http.Request) {
    // 完成和Client HTTP >>> WebSocket的協議升級
    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Print("upgrade:", err)
        return
    }
    defer c.Close()
    for {
        // 接收客戶端message
        mt, message, err := c.ReadMessage()
        if err != nil {
            log.Println("read:", err)
            break
        }
        log.Printf("recv: %s", message)
        // 向客戶端發送message
        err = c.WriteMessage(mt, message)
        if err != nil {
            log.Println("write:", err)
            break
        }
    }
}

func main() {
    flag.Parse()
    log.SetFlags(0)
    http.HandleFunc("/echo", echo)
    log.Fatal(http.ListenAndServe(*addr, nil))
}

更多示例可以參考 https://github.com/gorilla/websocket/tree/master/examples

總結

gorilla/websocket 庫是 websocket 協議的一種實現,相比標準庫的實現,封裝了協議細節,使用者關注 message 粒度的 API 即可,但需要注意 message 的讀寫 API 非併發安全,使用時注意不要多個協程併發調用。

參考資料

  1. https://github.com/gorilla/websocket
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://gocn.vip/topics/12349