網頁

2024/1/11

Golang WebSocket Ping Pong範例

WebSocket Ping Pong中文通常又稱為心跳(heartbeat),是指WebSocket協議的控制碼(opcode),代表Control frame(控制幀/控制框)的Ping frame與Pong frame,用以偵測客戶端和伺服器間的連線是否存在,以實現長連線(Keep-Alive)的機制。參考RFC6455



事前要求

參考「Golang WebSocket hello world」了解gorilla/websocket實現WebSocket的做法。


範例

下面範例的ping()函式中透過定時器time.Ticker,定時使用websocket.Conn.WriteMessage寫出一個Ping訊息並夾帶"ping"字串的payload(又稱Application data)到客戶端(瀏覽器)。

有實作WebSocket協議Ping Pong的客戶端(例如Chrome瀏覽器)在收到Ping訊息後,會立刻回應一個Pong訊息並把Ping payload一樣的內容回送給伺服端。handlePong()函式中,使用websocket.Conn.SetPongHandler設定一個處理Pong訊息的函式。

read()函式中使用一個for無窮迴圈中呼叫Conn.ReadMessage持續讀取client送來的訊息。

main.go

package main

import (
    "fmt"
    "net/http"
    "time"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true // allow CORS request's Origin header
    },
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

// websocket handler
func handler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil) // upgrade to a websocket connection
    if err != nil {
        panic(err)
    }

    go ping(conn)
    handlePong(conn)
    read(conn)
}

const (
    PING = "ping"
    PONG = "pong"

    pongWait   = 5 * time.Second
    pingPeriod = pongWait - 1
)

func ping(conn *websocket.Conn) {
    ticker := time.NewTicker(pingPeriod)
    defer func() {
        ticker.Stop()
        conn.Close()
    }()
    fmt.Println("websocket connection openned")

    for range ticker.C {
        err := conn.WriteMessage(websocket.PingMessage, []byte(PING))
        if err != nil {
            return
        }
    }
}

func handlePong(conn *websocket.Conn) {
    conn.SetPongHandler(func(appData string) error {
        if appData == PING {
            fmt.Println(PONG)
        }
        return nil
    })
}

func read(conn *websocket.Conn) {
    defer func() {
        conn.Close()
        fmt.Println("websocket connection closed")
    }()

    for {
        _, message, err := conn.ReadMessage() // read message from client
        if err != nil {
            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
                fmt.Printf("error: %v", err)
            }
            break
        }
        fmt.Printf("receive: %s\n", message)
    }
}

github



測試

啟動Go應用程式,Chrome瀏覽器安裝Simple WebSocket Client並打開。在[Server Location]的[URL]欄位輸入ws://localhost:8080/然後按Open開啟WebSocket連線,然後觀察Go應用程式會每5秒印出"pong"直到連線關閉(例如關閉Simple WebSocket Client分頁)。

websocket connection openned
pong
pong
...
websocket connection closed


沒有留言:

張貼留言