這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在Google官方維護的code.google.com\p\go.net\websocket包中的server.go檔案中,曾經有這麼一段描述:
// Handler is a simple interface to a WebSocket browser client.
// It checks if Origin header is valid URL by default.
// You might want to verify websocket.Conn.Config().Origin in the func.
// If you use Server instead of Handler, you could call websocket.Origin and
// check the origin in your Handshake func. So, if you want to accept
// non-browser client, which doesn't send Origin header, you could use Server
//. that doesn't check origin in its Handshake.
這裡說到,Handler是一個針對Websocket瀏覽器用戶端的簡單介面,預設情況下,Handler會檢查Http請求的標頭檔的Origin是否是一個有效值。最後說到,如果你想接收一個並不帶有Origin欄位資訊的非瀏覽器用戶端發送的websocket請求,你應該使用Server,使用Server不會在Websocket握手時對Origin進行檢查。
也許我們會問,什麼是瀏覽器用戶端,而什麼又是非瀏覽器用戶端,筆者對此研究的不深,只是在開發中發現,在進行go web編程時,由於go本身可以使用template模板向使用者推送頁面,比如使用者在用戶端輸入127.0.0.1:9000,伺服器收到這個請求,就會發送網站的首頁給使用者(這裡假設127.0.0.1:9000的/,這個路由對應一個首頁的get請求)。筆者認為凡是通過go編寫的web服務端程式推送給使用者的這個網頁就是瀏覽器用戶端。而除此之外的其他的非推送的網頁請求,以及一些手機(andriod、ios的手機)用戶端發送的請求都屬於非瀏覽器用戶端。
那我們又會問,如果是瀏覽器用戶端,該如何使用Handler,而非瀏覽器用戶端,該如何使用Server呢?下面就簡單舉例說一下:
先說瀏覽器用戶端的監聽websocket編程的寫法,代碼執行個體為:
package main
import (
"bufio"
"code.google.com/p/go.net/websocket"
"container/list"
"fmt"
"io"
"net/http"
)
var connid int
var conns *list.List
func ChatroomServer(ws *websocket.Conn) {
defer ws.Close()
connid++
id := connid
fmt.Printf("connection id: %d\n", id)
item := conns.PushBack(ws)
conns.Remove(item)
name := fmt.Sprintf("user%d", id)
SendMessage(nil, fmt.Sprintf("welcome %s join\n", name))
r := bufio.NewReader(ws)
for {
data, err := r.ReadBytes('\n')
if err != nil {
fmt.Printf("disconnected id: %d\n", id)
SendMessage(item, fmt.Sprintf("%s offline\n", name))
break
}
fmt.Printf("%s: %s", name, data)
SendMessage(item, fmt.Sprintf("%s\t> %s", name, data))
}
}
func SendMessage(self *list.Element, data string) {
for item := conns.Front(); item != nil; item = item.Next() {
ws, ok := item.Value.(*websocket.Conn)
if !ok {
panic("item not *websocket.Conn")
}
if item == self {
continue
}
io.WriteString(ws, data)
}
}
func Client(w http.ResponseWriter, r *http.Request) {
html := `<!doctype html>
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>golang websocket chatroom</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <script> var ws = new WebSocket("ws://127.0.0.1:6611/chatroom"); ws.onopen = function(e){ console.log("onopen"); console.dir(e); }; ws.onmessage = function(e){ console.log("onmessage"); console.dir(e); $('#log').append('<p>'+e.data+'<p>'); $('#log').get(0).scrollTop = $('#log').get(0).scrollHeight; }; ws.onclose = function(e){ console.log("onclose"); console.dir(e); }; ws.onerror = function(e){ console.log("onerror"); console.dir(e); }; $(function(){ $('#msgform').submit(function(){ ws.send($('#msg').val()+"\n"); $('#log').append('<p style="color:red;">My > '+$('#msg').val()+'<p>'); $('#log').get(0).scrollTop = $('#log').get(0).scrollHeight; $('#msg').val(''); return false; }); }); </script> </head> <body> <div id="log" style="height: 300px;overflow-y: scroll;border: 1px solid #CCC;"></div> <div> <form id="msgform"> <input type="text" id="msg" size="60" /> </form> </div> </body> </html>`
io.WriteString(w, html)
}
func main() {
fmt.Printf(`Welcome chatroom server! `)
connid = 0
conns = list.New()
http.Handle("/chatroom", websocket.Handler(ChatroomServer))
http.HandleFunc("/", Client)
err := http.ListenAndServe(":9090", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
以上是一個完整的針對瀏覽器用戶端發送websocket的伺服器代碼,可以看到,當使用者請求路由為/時,伺服器推送一個頁面給使用者,這個頁面含有websocket通訊端,然後收到這個頁面的使用者,就可以在這個頁面是輸入資訊發往後台,發送時使用的就是websocket。main函數中展示了如何使用websocket.Handler.
下面說一下非瀏覽器用戶端的寫法,這裡只修改了main函數,為了能夠同時監聽http請求和websocket請求,與上面不同,這裡使用了多協程的方式實現,同時這裡的websocket監聽無須路由,所有的websocket,無論路由是多少,都將被監聽到,代碼如下:
func main() {
fmt.Printf(`Welcome chatroom server! `)
connid = 0
conns = list.New()
http.HandleFunc("/", Client)
go func(){
err := http.ListenAndServe(":9090", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
err := http.ListenAndServe(":9090", websocket.Server{websocket.Config{},nil,ChatroomServer})
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
完結