From 31fbd524622a3209da5c265b7db924c23d333aa3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 10 Sep 2013 18:38:40 +0800 Subject: [PATCH] add chat example --- example/chat/chat.go | 42 ------- example/chat/conf/app.conf | 3 + example/chat/controllers/default.go | 14 +++ example/chat/controllers/ws.go | 169 ++++++++++++++++++++++++++++ example/chat/main.go | 12 ++ example/chat/views/index.html | 81 ------------- example/chat/views/index.tpl | 92 +++++++++++++++ 7 files changed, 290 insertions(+), 123 deletions(-) delete mode 100644 example/chat/chat.go create mode 100644 example/chat/conf/app.conf create mode 100644 example/chat/controllers/default.go create mode 100644 example/chat/controllers/ws.go create mode 100644 example/chat/main.go delete mode 100644 example/chat/views/index.html create mode 100644 example/chat/views/index.tpl diff --git a/example/chat/chat.go b/example/chat/chat.go deleted file mode 100644 index 09005753..00000000 --- a/example/chat/chat.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "fmt" - "github.com/astaxie/beego" - "github.com/fzzy/sockjs-go/sockjs" - "strings" -) - -var users *sockjs.SessionPool = sockjs.NewSessionPool() - -func chatHandler(s sockjs.Session) { - users.Add(s) - defer users.Remove(s) - - for { - m := s.Receive() - if m == nil { - break - } - fullAddr := s.Info().RemoteAddr - addr := fullAddr[:strings.LastIndex(fullAddr, ":")] - m = []byte(fmt.Sprintf("%s: %s", addr, m)) - users.Broadcast(m) - } -} - -type MainController struct { - beego.Controller -} - -func (m *MainController) Get() { - m.TplNames = "index.html" -} - -func main() { - conf := sockjs.NewConfig() - sockjshandler := sockjs.NewHandler("/chat", chatHandler, conf) - beego.Router("/", &MainController{}) - beego.RouterHandler("/chat/:info(.*)", sockjshandler) - beego.Run() -} diff --git a/example/chat/conf/app.conf b/example/chat/conf/app.conf new file mode 100644 index 00000000..9d5a823f --- /dev/null +++ b/example/chat/conf/app.conf @@ -0,0 +1,3 @@ +appname = chat +httpport = 8080 +runmode = dev diff --git a/example/chat/controllers/default.go b/example/chat/controllers/default.go new file mode 100644 index 00000000..d53bd337 --- /dev/null +++ b/example/chat/controllers/default.go @@ -0,0 +1,14 @@ +package controllers + +import ( + "github.com/astaxie/beego" +) + +type MainController struct { + beego.Controller +} + +func (this *MainController) Get() { + this.Data["host"] = this.Ctx.Request.Host + this.TplNames = "index.tpl" +} diff --git a/example/chat/controllers/ws.go b/example/chat/controllers/ws.go new file mode 100644 index 00000000..9334336f --- /dev/null +++ b/example/chat/controllers/ws.go @@ -0,0 +1,169 @@ +package controllers + +import ( + "github.com/astaxie/beego" + "github.com/garyburd/go-websocket/websocket" + "io/ioutil" + "math/rand" + "net/http" + "time" +) + +const ( + // Time allowed to write a message to the client. + writeWait = 10 * time.Second + + // Time allowed to read the next message from the client. + readWait = 60 * time.Second + + // Send pings to client with this period. Must be less than readWait. + pingPeriod = (readWait * 9) / 10 + + // Maximum message size allowed from client. + maxMessageSize = 512 +) + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) + go h.run() +} + +// connection is an middleman between the websocket connection and the hub. +type connection struct { + username string + + // The websocket connection. + ws *websocket.Conn + + // Buffered channel of outbound messages. + send chan []byte +} + +// readPump pumps messages from the websocket connection to the hub. +func (c *connection) readPump() { + defer func() { + h.unregister <- c + c.ws.Close() + }() + c.ws.SetReadLimit(maxMessageSize) + c.ws.SetReadDeadline(time.Now().Add(readWait)) + for { + op, r, err := c.ws.NextReader() + if err != nil { + break + } + switch op { + case websocket.OpPong: + c.ws.SetReadDeadline(time.Now().Add(readWait)) + case websocket.OpText: + message, err := ioutil.ReadAll(r) + if err != nil { + break + } + h.broadcast <- []byte(c.username + "_" + time.Now().Format("15:04:05") + ":" + string(message)) + } + } +} + +// write writes a message with the given opCode and payload. +func (c *connection) write(opCode int, payload []byte) error { + c.ws.SetWriteDeadline(time.Now().Add(writeWait)) + return c.ws.WriteMessage(opCode, payload) +} + +// writePump pumps messages from the hub to the websocket connection. +func (c *connection) writePump() { + ticker := time.NewTicker(pingPeriod) + defer func() { + ticker.Stop() + c.ws.Close() + }() + for { + select { + case message, ok := <-c.send: + if !ok { + c.write(websocket.OpClose, []byte{}) + return + } + if err := c.write(websocket.OpText, message); err != nil { + return + } + case <-ticker.C: + if err := c.write(websocket.OpPing, []byte{}); err != nil { + return + } + } + } +} + +type hub struct { + // Registered connections. + connections map[*connection]bool + + // Inbound messages from the connections. + broadcast chan []byte + + // Register requests from the connections. + register chan *connection + + // Unregister requests from connections. + unregister chan *connection +} + +var h = &hub{ + broadcast: make(chan []byte, maxMessageSize), + register: make(chan *connection, 1), + unregister: make(chan *connection, 1), + connections: make(map[*connection]bool), +} + +func (h *hub) run() { + for { + select { + case c := <-h.register: + h.connections[c] = true + case c := <-h.unregister: + delete(h.connections, c) + close(c.send) + case m := <-h.broadcast: + for c := range h.connections { + select { + case c.send <- m: + default: + close(c.send) + delete(h.connections, c) + } + } + } + } +} + +type WSController struct { + beego.Controller +} + +func (this *WSController) Get() { + ws, err := websocket.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request.Header, nil, 1024, 1024) + if _, ok := err.(websocket.HandshakeError); ok { + http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) + return + } else if err != nil { + return + } + c := &connection{send: make(chan []byte, 256), ws: ws, username: randomString(10)} + h.register <- c + go c.writePump() + c.readPump() +} + +func randomString(l int) string { + bytes := make([]byte, l) + for i := 0; i < l; i++ { + bytes[i] = byte(randInt(65, 90)) + } + return string(bytes) +} + +func randInt(min int, max int) int { + return min + rand.Intn(max-min) +} diff --git a/example/chat/main.go b/example/chat/main.go new file mode 100644 index 00000000..806fe145 --- /dev/null +++ b/example/chat/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/astaxie/beego" + "github.com/astaxie/beego/example/chat/controllers" +) + +func main() { + beego.Router("/", &controllers.MainController{}) + beego.Router("/ws", &controllers.WSController{}) + beego.Run() +} diff --git a/example/chat/views/index.html b/example/chat/views/index.html deleted file mode 100644 index 12e5616b..00000000 --- a/example/chat/views/index.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - Sockjs-go chat - - -

Sockjs-go chat

- -
- Status: disconnected -
-
-
-
- - - -
- - \ No newline at end of file diff --git a/example/chat/views/index.tpl b/example/chat/views/index.tpl new file mode 100644 index 00000000..75bffa62 --- /dev/null +++ b/example/chat/views/index.tpl @@ -0,0 +1,92 @@ + + + +Chat Example + + + + + +
+
+ + +
+ + \ No newline at end of file