31fbd524 by astaxie

add chat example

1 parent a88750d2
1 package main
2
3 import (
4 "fmt"
5 "github.com/astaxie/beego"
6 "github.com/fzzy/sockjs-go/sockjs"
7 "strings"
8 )
9
10 var users *sockjs.SessionPool = sockjs.NewSessionPool()
11
12 func chatHandler(s sockjs.Session) {
13 users.Add(s)
14 defer users.Remove(s)
15
16 for {
17 m := s.Receive()
18 if m == nil {
19 break
20 }
21 fullAddr := s.Info().RemoteAddr
22 addr := fullAddr[:strings.LastIndex(fullAddr, ":")]
23 m = []byte(fmt.Sprintf("%s: %s", addr, m))
24 users.Broadcast(m)
25 }
26 }
27
28 type MainController struct {
29 beego.Controller
30 }
31
32 func (m *MainController) Get() {
33 m.TplNames = "index.html"
34 }
35
36 func main() {
37 conf := sockjs.NewConfig()
38 sockjshandler := sockjs.NewHandler("/chat", chatHandler, conf)
39 beego.Router("/", &MainController{})
40 beego.RouterHandler("/chat/:info(.*)", sockjshandler)
41 beego.Run()
42 }
1 appname = chat
2 httpport = 8080
3 runmode = dev
1 package controllers
2
3 import (
4 "github.com/astaxie/beego"
5 )
6
7 type MainController struct {
8 beego.Controller
9 }
10
11 func (this *MainController) Get() {
12 this.Data["host"] = this.Ctx.Request.Host
13 this.TplNames = "index.tpl"
14 }
1 package controllers
2
3 import (
4 "github.com/astaxie/beego"
5 "github.com/garyburd/go-websocket/websocket"
6 "io/ioutil"
7 "math/rand"
8 "net/http"
9 "time"
10 )
11
12 const (
13 // Time allowed to write a message to the client.
14 writeWait = 10 * time.Second
15
16 // Time allowed to read the next message from the client.
17 readWait = 60 * time.Second
18
19 // Send pings to client with this period. Must be less than readWait.
20 pingPeriod = (readWait * 9) / 10
21
22 // Maximum message size allowed from client.
23 maxMessageSize = 512
24 )
25
26 func init() {
27 rand.Seed(time.Now().UTC().UnixNano())
28 go h.run()
29 }
30
31 // connection is an middleman between the websocket connection and the hub.
32 type connection struct {
33 username string
34
35 // The websocket connection.
36 ws *websocket.Conn
37
38 // Buffered channel of outbound messages.
39 send chan []byte
40 }
41
42 // readPump pumps messages from the websocket connection to the hub.
43 func (c *connection) readPump() {
44 defer func() {
45 h.unregister <- c
46 c.ws.Close()
47 }()
48 c.ws.SetReadLimit(maxMessageSize)
49 c.ws.SetReadDeadline(time.Now().Add(readWait))
50 for {
51 op, r, err := c.ws.NextReader()
52 if err != nil {
53 break
54 }
55 switch op {
56 case websocket.OpPong:
57 c.ws.SetReadDeadline(time.Now().Add(readWait))
58 case websocket.OpText:
59 message, err := ioutil.ReadAll(r)
60 if err != nil {
61 break
62 }
63 h.broadcast <- []byte(c.username + "_" + time.Now().Format("15:04:05") + ":" + string(message))
64 }
65 }
66 }
67
68 // write writes a message with the given opCode and payload.
69 func (c *connection) write(opCode int, payload []byte) error {
70 c.ws.SetWriteDeadline(time.Now().Add(writeWait))
71 return c.ws.WriteMessage(opCode, payload)
72 }
73
74 // writePump pumps messages from the hub to the websocket connection.
75 func (c *connection) writePump() {
76 ticker := time.NewTicker(pingPeriod)
77 defer func() {
78 ticker.Stop()
79 c.ws.Close()
80 }()
81 for {
82 select {
83 case message, ok := <-c.send:
84 if !ok {
85 c.write(websocket.OpClose, []byte{})
86 return
87 }
88 if err := c.write(websocket.OpText, message); err != nil {
89 return
90 }
91 case <-ticker.C:
92 if err := c.write(websocket.OpPing, []byte{}); err != nil {
93 return
94 }
95 }
96 }
97 }
98
99 type hub struct {
100 // Registered connections.
101 connections map[*connection]bool
102
103 // Inbound messages from the connections.
104 broadcast chan []byte
105
106 // Register requests from the connections.
107 register chan *connection
108
109 // Unregister requests from connections.
110 unregister chan *connection
111 }
112
113 var h = &hub{
114 broadcast: make(chan []byte, maxMessageSize),
115 register: make(chan *connection, 1),
116 unregister: make(chan *connection, 1),
117 connections: make(map[*connection]bool),
118 }
119
120 func (h *hub) run() {
121 for {
122 select {
123 case c := <-h.register:
124 h.connections[c] = true
125 case c := <-h.unregister:
126 delete(h.connections, c)
127 close(c.send)
128 case m := <-h.broadcast:
129 for c := range h.connections {
130 select {
131 case c.send <- m:
132 default:
133 close(c.send)
134 delete(h.connections, c)
135 }
136 }
137 }
138 }
139 }
140
141 type WSController struct {
142 beego.Controller
143 }
144
145 func (this *WSController) Get() {
146 ws, err := websocket.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request.Header, nil, 1024, 1024)
147 if _, ok := err.(websocket.HandshakeError); ok {
148 http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400)
149 return
150 } else if err != nil {
151 return
152 }
153 c := &connection{send: make(chan []byte, 256), ws: ws, username: randomString(10)}
154 h.register <- c
155 go c.writePump()
156 c.readPump()
157 }
158
159 func randomString(l int) string {
160 bytes := make([]byte, l)
161 for i := 0; i < l; i++ {
162 bytes[i] = byte(randInt(65, 90))
163 }
164 return string(bytes)
165 }
166
167 func randInt(min int, max int) int {
168 return min + rand.Intn(max-min)
169 }
1 package main
2
3 import (
4 "github.com/astaxie/beego"
5 "github.com/astaxie/beego/example/chat/controllers"
6 )
7
8 func main() {
9 beego.Router("/", &controllers.MainController{})
10 beego.Router("/ws", &controllers.WSController{})
11 beego.Run()
12 }
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
5 <script src="http://cdn.sockjs.org/sockjs-0.3.4.min.js"></script>
6 <script>
7 $(function() {
8 var conn = null;
9
10 function log(msg) {
11 var control = $('#log');
12 control.html(control.html() + msg + '<br/>');
13 control.scrollTop(control.scrollTop() + 1000);
14 }
15
16 function disconnect() {
17 if (conn != null) {
18 log('Disconnecting...');
19
20 conn.close();
21 conn = null;
22
23 updateUi();
24 }
25 }
26
27 function updateUi() {
28 if (conn == null || conn.readyState != SockJS.OPEN) {
29 $('#status').text('disconnected');
30 $('#connect').text('Connect');
31 } else {
32 $('#status').text('connected (' + conn.protocol + ')');
33 $('#connect').text('Disconnect');
34 }
35 }
36
37 $('form').submit(function() {
38 var text = $('#message').val();
39 conn.send(text);
40 $('#message').val('').focus();
41 return false;
42 });
43
44 conn = new SockJS('http://' + window.location.host + '/chat');
45 log('Connecting...');
46
47 conn.onopen = function() {
48 log('Connected.');
49 updateUi();
50 };
51
52 conn.onmessage = function(e) {
53 log(e.data);
54 };
55
56 conn.onclose = function() {
57 log('Disconnected.');
58 conn = null;
59 updateUi();
60 };
61
62 $('#message').val('').focus();
63 });
64 </script>
65 <title>Sockjs-go chat</title>
66 </head>
67 <body>
68 <h1>Sockjs-go chat</h1>
69
70 <div>
71 Status: <span id="status">disconnected</span>
72 </div>
73 <div id="log" style="width: 60em; height: 20em; overflow:auto; border: 1px solid black">
74 </div>
75 <form id="chatform">
76 <label for="message">Message:</label>
77 <input id="message" type="text" />
78 <input type="submit" value="Send" />
79 </form>
80 </body>
81 </html>
...\ No newline at end of file ...\ No newline at end of file
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <title>Chat Example</title>
5 <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
6 <script type="text/javascript">
7 $(function() {
8
9 var conn;
10 var msg = $("#msg");
11 var log = $("#log");
12
13 function appendLog(msg) {
14 var d = log[0]
15 var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
16 msg.appendTo(log)
17 if (doScroll) {
18 d.scrollTop = d.scrollHeight - d.clientHeight;
19 }
20 }
21
22 $("#form").submit(function() {
23 if (!conn) {
24 return false;
25 }
26 if (!msg.val()) {
27 return false;
28 }
29 conn.send(msg.val());
30 msg.val("");
31 return false
32 });
33
34 if (window["WebSocket"]) {
35 conn = new WebSocket("ws://{{.host}}/ws");
36 conn.onclose = function(evt) {
37 appendLog($("<div><b>Connection closed.</b></div>"))
38 }
39 conn.onmessage = function(evt) {
40 appendLog($("<div/>").text(evt.data))
41 }
42 } else {
43 appendLog($("<div><b>Your browser does not support WebSockets.</b></div>"))
44 }
45 });
46 </script>
47 <style type="text/css">
48 html {
49 overflow: hidden;
50 }
51
52 body {
53 overflow: hidden;
54 padding: 0;
55 margin: 0;
56 width: 100%;
57 height: 100%;
58 background: gray;
59 }
60
61 #log {
62 background: white;
63 margin: 0;
64 padding: 0.5em 0.5em 0.5em 0.5em;
65 position: absolute;
66 top: 0.5em;
67 left: 0.5em;
68 right: 0.5em;
69 bottom: 3em;
70 overflow: auto;
71 }
72
73 #form {
74 padding: 0 0.5em 0 0.5em;
75 margin: 0;
76 position: absolute;
77 bottom: 1em;
78 left: 0px;
79 width: 100%;
80 overflow: hidden;
81 }
82
83 </style>
84 </head>
85 <body>
86 <div id="log"></div>
87 <form id="form">
88 <input type="submit" value="Send" />
89 <input type="text" id="msg" size="64"/>
90 </form>
91 </body>
92 </html>
...\ No newline at end of file ...\ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!