Jex’s Note

Golang Websocket

Introduction

目前主要 golang 的 websocket 套件有兩個, 分别是官方維護的 code.google.com/p/go.net/websocket,

及非官方版本的 github.com/gorilla/websocket, 我也不知道哪個比較好,

這邊有個比較表可以參考看看

官方版的在使用 go get 下載前還需要先下載 Mercurial 這個版控, 否則無法下載

以下分别使用這兩種套件實現 websocket 的 example

HTML + JS :

<!DOCTYPE html>
<head>
    <title>Test~</title>
</head>
<body>
<script type="text/javascript">
    ws = new WebSocket("ws://54.250.138.78:9090/connws/");
    ws.onopen = function() {
        console.log("[onopen] connect ws uri.");
        var data = {
            "Enabled" : "true"
        };
        ws.send(JSON.stringify(data));
    }
    ws.onmessage = function(e) {
        var res = JSON.parse(e.data);
        console.log(res);
    }
    ws.onclose = function(e) {
        console.log("[onclose] connection closed (" + e.code + ")");
        delete ws;
    }
    ws.onerror = function (e) {
        console.log("[onerror] error!");
    }
</script>
</body>
</html>

[1] 以 go.net/websocket 實作

main :

http.Handle("/connws/", websocket.Handler(ConnWs))
err := http.ListenAndServe(":9090", nil)
if err != nil {
    log.Fatal("ListenAndServe: ", err)
}

func ConnWs(ws *websocket.Conn) :

var err error
rec := map[string] interface{}{}

for {
    err = websocket.JSON.Receive(ws, &rec)
    if err != nil {
        fmt.Println(err.Error())
        ws.Close()
        break
    }
    fmt.Printf("Server received : %v\n", rec)

    if err = websocket.JSON.Send(ws, rec); err != nil {
        fmt.Println("Fail to send message.")
        ws.Close()
        break
    }
}

result :

$ go run main.go
Server received : map[Enabled:true]

[2] 以 gorilla/websocket 實作

main :

http.HandleFunc("/connws/", ConnWs)
err := http.ListenAndServe(":9090", nil)
if err != nil {
    log.Fatal("ListenAndServe: ", err)
}

func ConnWs(w http.ResponseWriter, r *http.Request) :

ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
if _, ok := err.(websocket.HandshakeError); ok {
    http.Error(w, "Not a websocket handshake", 400)
    return
} else if err != nil {
    log.Println(err)
    return
}

rec := map[string] interface{}{}
for {
    if err = ws.ReadJSON(&rec); err != nil {
        if err.Error() == "EOF" {
            return
        }
        // ErrShortWrite means that a write accepted fewer bytes than requested but failed to return an explicit error.
        if err.Error() == "unexpected EOF" {
            return
        }
        fmt.Println("Read : " + err.Error())
        return
    }
    rec["Test"] = "tt"
    fmt.Println(rec)
    if err = ws.WriteJSON(&rec); err != nil {
        fmt.Println("Write : " + err.Error())
        return
    }
}

result :

$ go run main.go
map[Enabled:true Test:tt]

有特別處理 io error 的 EOF, 否則頁面 refresh 會陷入無窮迴圈

Cross domain

原本是使用官方的版本, 直到有 cross domain 的需求,

HTML5 websocket 本身是支持 cross domain 的,

但是官方版本不知道怎麼去支持它, 一直得到 403,

最後發現 gorilla/websocket 是支持的, 就改用它來達成

Comments