FreeBSD 中文教學相對現在很冷門...正體中文幾乎已經沒有人在寫新文件資料了...
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
至於用其他家產的憑證就必須自己去找看看對應的來試試看嘍