mirror of
https://github.com/astaxie/beego.git
synced 2024-11-04 21:00:55 +00:00
add chat example
This commit is contained in:
parent
a88750d2b3
commit
31fbd52462
@ -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()
|
|
||||||
}
|
|
3
example/chat/conf/app.conf
Normal file
3
example/chat/conf/app.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
appname = chat
|
||||||
|
httpport = 8080
|
||||||
|
runmode = dev
|
14
example/chat/controllers/default.go
Normal file
14
example/chat/controllers/default.go
Normal file
@ -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"
|
||||||
|
}
|
169
example/chat/controllers/ws.go
Normal file
169
example/chat/controllers/ws.go
Normal file
@ -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)
|
||||||
|
}
|
12
example/chat/main.go
Normal file
12
example/chat/main.go
Normal file
@ -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()
|
||||||
|
}
|
@ -1,81 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
|
||||||
<script src="http://cdn.sockjs.org/sockjs-0.3.4.min.js"></script>
|
|
||||||
<script>
|
|
||||||
$(function() {
|
|
||||||
var conn = null;
|
|
||||||
|
|
||||||
function log(msg) {
|
|
||||||
var control = $('#log');
|
|
||||||
control.html(control.html() + msg + '<br/>');
|
|
||||||
control.scrollTop(control.scrollTop() + 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnect() {
|
|
||||||
if (conn != null) {
|
|
||||||
log('Disconnecting...');
|
|
||||||
|
|
||||||
conn.close();
|
|
||||||
conn = null;
|
|
||||||
|
|
||||||
updateUi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateUi() {
|
|
||||||
if (conn == null || conn.readyState != SockJS.OPEN) {
|
|
||||||
$('#status').text('disconnected');
|
|
||||||
$('#connect').text('Connect');
|
|
||||||
} else {
|
|
||||||
$('#status').text('connected (' + conn.protocol + ')');
|
|
||||||
$('#connect').text('Disconnect');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('form').submit(function() {
|
|
||||||
var text = $('#message').val();
|
|
||||||
conn.send(text);
|
|
||||||
$('#message').val('').focus();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
conn = new SockJS('http://' + window.location.host + '/chat');
|
|
||||||
log('Connecting...');
|
|
||||||
|
|
||||||
conn.onopen = function() {
|
|
||||||
log('Connected.');
|
|
||||||
updateUi();
|
|
||||||
};
|
|
||||||
|
|
||||||
conn.onmessage = function(e) {
|
|
||||||
log(e.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
conn.onclose = function() {
|
|
||||||
log('Disconnected.');
|
|
||||||
conn = null;
|
|
||||||
updateUi();
|
|
||||||
};
|
|
||||||
|
|
||||||
$('#message').val('').focus();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<title>Sockjs-go chat</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Sockjs-go chat</h1>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Status: <span id="status">disconnected</span>
|
|
||||||
</div>
|
|
||||||
<div id="log" style="width: 60em; height: 20em; overflow:auto; border: 1px solid black">
|
|
||||||
</div>
|
|
||||||
<form id="chatform">
|
|
||||||
<label for="message">Message:</label>
|
|
||||||
<input id="message" type="text" />
|
|
||||||
<input type="submit" value="Send" />
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
92
example/chat/views/index.tpl
Normal file
92
example/chat/views/index.tpl
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Chat Example</title>
|
||||||
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
var conn;
|
||||||
|
var msg = $("#msg");
|
||||||
|
var log = $("#log");
|
||||||
|
|
||||||
|
function appendLog(msg) {
|
||||||
|
var d = log[0]
|
||||||
|
var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
|
||||||
|
msg.appendTo(log)
|
||||||
|
if (doScroll) {
|
||||||
|
d.scrollTop = d.scrollHeight - d.clientHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#form").submit(function() {
|
||||||
|
if (!conn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!msg.val()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
conn.send(msg.val());
|
||||||
|
msg.val("");
|
||||||
|
return false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (window["WebSocket"]) {
|
||||||
|
conn = new WebSocket("ws://{{.host}}/ws");
|
||||||
|
conn.onclose = function(evt) {
|
||||||
|
appendLog($("<div><b>Connection closed.</b></div>"))
|
||||||
|
}
|
||||||
|
conn.onmessage = function(evt) {
|
||||||
|
appendLog($("<div/>").text(evt.data))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
appendLog($("<div><b>Your browser does not support WebSockets.</b></div>"))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style type="text/css">
|
||||||
|
html {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log {
|
||||||
|
background: white;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5em;
|
||||||
|
left: 0.5em;
|
||||||
|
right: 0.5em;
|
||||||
|
bottom: 3em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form {
|
||||||
|
padding: 0 0.5em 0 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1em;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<form id="form">
|
||||||
|
<input type="submit" value="Send" />
|
||||||
|
<input type="text" id="msg" size="64"/>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user