golang 請直接上 golang 官網找最新版本對應改一下喔!!
用套件管理工具 pkg 安裝的很可能不是最新版所以直接上官網抓會比較好...
wget https://storage.googleapis.com/golang/go1.8.3.freebsd-amd64.tar.gz tar zxvf go1.8.3.freebsd-amd64.tar.gz sudo mv go /usr/local/ mkdir gowork vi .cshrc ADD setenv GOROOT /usr/local/go setenv GOPATH $HOME/gowork set path = (/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin $HOME/bin $GOROOT/bin) source .cshrc go
已經可以用了!!
golang 在各平台上幾乎都能簡易的解壓縮設定環境變數後就可以用了... 同一套 source code 搬過去直接 go build .... 就可以編出一個可執行檔... 某些應用上是蠻好用的
抓取安裝 websocket 程式庫
go get github.com/gorilla/websocket cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/command`
command 光用這個範例就可以延伸想像做許多有趣 or 邪惡應用... XDD
但為了方便測試會用 echo 這 server client 的範例 code
cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/command` && cd ../echo
但現在應用上會碰到的問題就是 https 下跨接 ws 由 SSL 加密模式接 未加密的任何東西時瀏覽器會擋...當然可以設 header by pass 的規則但還是不完整的做法
所以必須要能使用 wss 也是相同加密模式的 websocket
由G大神找到 wss 也就是加密傳輸的 websocket 範例中都沒有完整的寫到 直接可以 run 起來的問題點
rootPEM
拼湊了幾個後得到
server_wss.go // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build ignore 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{} // use default options func echo(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Print("upgrade:", err) return } defer c.Close() for { mt, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) break } log.Printf("recv: %s", message) err = c.WriteMessage(mt, message) if err != nil { log.Println("write:", err) break } } } func home(w http.ResponseWriter, r *http.Request) { homeTemplate.Execute(w, "wss://"+r.Host+"/echo") } func main() { flag.Parse() log.SetFlags(0) http.HandleFunc("/echo", echo) http.HandleFunc("/", home) //log.Fatal(http.ListenAndServe(*addr, nil)) log.Fatal(http.ListenAndServeTLS(*addr, "cert.pem", "privkey.pem", nil)) } var homeTemplate = template.Must(template.New("").Parse(` <!DOCTYPE html> <head> <meta charset="utf-8"> <script> window.addEventListener("load", function(evt) { var output = document.getElementById("output"); var input = document.getElementById("input"); var ws; var print = function(message) { var d = document.createElement("div"); d.innerHTML = message; output.appendChild(d); }; document.getElementById("open").onclick = function(evt) { if (ws) { return false; } ws = new WebSocket("{{.}}"); ws.onopen = function(evt) { print("OPEN"); } ws.onclose = function(evt) { print("CLOSE"); ws = null; } ws.onmessage = function(evt) { print("RESPONSE: " + evt.data); } ws.onerror = function(evt) { print("ERROR: " + evt.data); } return false; }; document.getElementById("send").onclick = function(evt) { if (!ws) { return false; } print("SEND: " + input.value); ws.send(input.value); return false; }; document.getElementById("close").onclick = function(evt) { if (!ws) { return false; } ws.close(); return false; }; }); </script> </head> <body> <table> <tr><td valign="top" width="50%"> <p>Click "Open" to create a connection to the server, "Send" to send a message to the server and "Close" to close the connection. You can change the message and send multiple times. <p> <form> <button id="open">Open</button> <button id="close">Close</button> <p><input id="input" type="text" value="Hello world!"> <button id="send">Send</button> </form> </td><td valign="top" width="50%"> <div id="output"></div> </td></tr></table> </body> </html> `)) client_wss.go // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build ignore package main import ( "crypto/tls" "crypto/x509" "flag" "log" "net/url" "os" "os/signal" "time" "io/ioutil" "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: "wss", Host: *addr, Path: "/echo"} log.Printf("connecting to %s", u.String()) //c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) rootPEM, err := ioutil.ReadFile("root.pem") if err != nil || rootPEM == nil { log.Fatal("dial:", err) } roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM(rootPEM) if !ok { log.Fatal("failed to parse root certificate") } d := websocket.Dialer{TLSClientConfig: &tls.Config{RootCAs: roots}} c, _, err := d.Dial(u.String(), nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() done := make(chan struct{}) go func() { defer c.Close() defer close(done) for { _, 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 t := <-ticker.C: err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) if err != nil { log.Println("write:", err) return } case <-interrupt: log.Println("interrupt") // To cleanly close a connection, a client should send a close // frame and wait for the server to close the connection. 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): } c.Close() return } } }
server_wss 大概比較沒什麼問題 只需要記得改一下 localhost:8080 to 0.0.0.0:8080 這樣外部才連的到 or 只是區網內就改成你 Server 內部的虛擬 IP... 跟要使用的 Port
另外
log.Fatal(http.ListenAndServeTLS(*addr, "cert.pem", "privkey.pem", nil))
cert.pem 跟 privkey.pem 憑證的部分
我是直接
-ticker>
ln -s /etc/dehydrated/certs/cert.pem ln -s /etc/dehydrated/certs/ privkey.pem
此時 go run server_wss.go 需要 root 權限!! 否則憑證無法讀取
PS: 這就看你自己憑證路徑是什麼在哪嘍~ 我是用 dehydrated 來取得 Let’s Encrypt 憑證
其他還有相仿的官方的...路徑不盡相同嘍
client_wss 基本上同上 localhost:8080 記得改成你 Domain Name
主要會碰到的問題回到 rootPEM !!!
這到底要給什麼??
由於我是用 Let’s Encrypt
所以上 https://letsencrypt.org/certificates/ 找 Active
Let’s Encrypt Authority X3 (IdenTrust cross-signed)
將它存成 root.pem
至於用其他家產的憑證就必須自己去找看看對應的來試試看嘍
沒有留言:
張貼留言