首页相关的网页请求路由如下:
# Login
GET / Application.Index
GET /demo Application.EnterDemo
首页显示输入昵称和三种聊天技术选择入口,选择后form提交到Application.EnterDemo页面。跳转到三种具体的聊天技术页面是通过Get参数增加user的方式进行的。
func (c Application) EnterDemo(user, demo string) revel.Result {
c.Validation.Required(user)
c.Validation.Required(demo)
if c.Validation.HasErrors() {
c.Flash.Error("Please choose a nick name and the demonstration type.")
return c.Redirect(Application.Index)
}
switch demo {
case "refresh":
return c.Redirect("/refresh?user=%s", user)
case"longpolling":
return c.Redirect("/longpolling/room?user=%s", user)
case"websocket":
return c.Redirect("/websocket/room?user=%s", user)
}
returnnil
}
从功能角度,定时刷新页面永远会带用户名。http://localhost:9000/refresh?user=ghj1976
在routes表中我们可以看到定时刷新涉及到下面几个页面请求:
# Refresh demo
GET /refresh Refresh.Index
GET /refresh/room Refresh.Room
POST /refresh/room Refresh.Say
GET /refresh/room/leave Refresh.Leave
进入页面,或者整理浏览器刷新页面,都会触发用户进入chatroom。
func (c Refresh) Index(user string) revel.Result {
chatroom.Join(user)
return c.Room(user)
}
每隔5秒钟,重新请求一下Refresh.Room页面。
<script type="text/javascript"charset="utf-8">
// Scroll the messages panel to the end
var scrollDown = function() {
$('#thread').scrollTo('max')
}
// Reload the whole messages panel
var refresh = function() {
$('#thread').load('/refresh/room?user={{.user}} #thread .message', function() {
scrollDown()
})
}
// Call refresh every 5 seconds
setInterval(refresh, 5000)
scrollDown()
</script>
Refresh.Room 的处理逻辑:
使用一个订阅者把目前聊天室的所有信息都显示出来。离开这个处理过程就把订阅者注销。
func (c Refresh) Room(user string) revel.Result {
subscription := chatroom.Subscribe()
defer subscription.Cancel()
events := subscription.Archive
for i, _ := range events {
if events[i].User == user {
events[i].User = "you"
}
}
return c.Render(user, events)
}
对 /refresh/room 发出Post请求是,则记录发出的消息,同时刷新页面。
func (c Refresh) Say(user, message string) revel.Result {
chatroom.Say(user, message)
return c.Room(user)
}
离开聊天室时,标示离开,同时跳转页面。
func (c Refresh) Leave(user string) revel.Result {
chatroom.Leave(user)
return c.Redirect(Application.Index)
}
长连接的路由请求如下:
# Long polling demo
GET /longpolling/room LongPolling.Room
GET /longpolling/room/messages LongPolling.WaitMessages
POST /longpolling/room/messages LongPolling.Say
GET /longpolling/room/leave LongPolling.Leave
当请求长连接的页面时,http://localhost:9000/longpolling/room?user=ghj1976 把用户加入聊天室。
func (c LongPolling) Room(user string) revel.Result {
chatroom.Join(user)
return c.Render(user)
}
长连接的浏览器端核心逻辑如下,删除了一些跟核心逻辑无关的代码:
向服务器请求有没有新的消息,如果没有新的消息,则会一直等待服务器。如果有则请求完成消息,然后再次发出一个请求getMessages();
<script type="text/javascript">
var lastReceived = 0
var waitMessages ='/longpolling/room/messages?lastReceived='
// Retrieve new messages
var getMessages = function() {
$.ajax({
url: waitMessages + lastReceived,
success: function(events) {
$(events).each(function() {
display(this)
lastReceived = this.Timestamp
})
getMessages()
},
dataType: 'json'
});
}
getMessages();
// Display a message
var display = function(event) {
$('#thread').append(tmpl('message_tmpl', {event: event}));
$('#thread').scrollTo('max')
}
</script>
服务器端处理逻辑则是借用了channel无法读出新的内容时,会一直等下去的技巧,代码如下:
func (c LongPolling) WaitMessages(lastReceived int) revel.Result {
subscription := chatroom.Subscribe()
defer subscription.Cancel()
// See if anything is new in the archive.
var events []chatroom.Event
for _, event := range subscription.Archive {
if event.Timestamp > lastReceived {
events = append(events, event)
}
}
// If we found one, grand.
if len(events) > 0 {
return c.RenderJson(events)
}
// Else, wait for something new.
event := <-subscription.New
return c.RenderJson([]chatroom.Event{event})
}
离开和发送消息的逻辑跟定时刷新的机制基本类似,就不再表述。
请求路由信息:
# WebSocket demo
GET /websocket/room WebSocket.Room
WS /websocket/room/socket WebSocket.RoomSocket
WebSocket.Room 处理请求这套机制的进入页面。
WebSocket是Chrome支持的一套通讯机制,javascript中只需要简单的 socket.onmessage 、socket.send 两个方法就可以完成相关工作。
<script type="text/javascript">
// Create a socket
var socket = new WebSocket('ws://'+window.location.host+'/websocket/room/socket?user={{.user}}')
// Display a message
var display = function(event) {
$('#thread').append(tmpl('message_tmpl', {event: event}));
$('#thread').scrollTo('max')
}
// Message received on the socket
socket.onmessage = function(event) {
display(JSON.parse(event.data))
}
$('#send').click(function(e) {
var message = $('#message').val()
$('#message').val('')
socket.send(message)
});
$('#message').keypress(function(e) {
if(e.charCode == 13 || e.keyCode == 13) {
$('#send').click()
e.preventDefault()
}
})
</script>
服务器端WebSocket 请求使用了 code.google.com/p/go.net/websocket 封装包。
代码实现分了三大块,先把用户加入聊天室,订阅聊天室,websocket.JSON.Send 发送之前已有的聊天信息,
然后使用 websocket.Message.Receive 等待接受来自浏览器的消息内容。
同时用 channel select 方式接受自己发出的和别人发出的消息。
func (c WebSocket) RoomSocket(user string, ws *websocket.Conn) revel.Result {
// Join the room.
subscription := chatroom.Subscribe()
defer subscription.Cancel()
chatroom.Join(user)
defer chatroom.Leave(user)
// Send down the archive.
for _, event := range subscription.Archive {
if websocket.JSON.Send(ws, &event) != nil {
// They disconnected
return nil
}
}
// In order to select between websocket messages and subscription events, we
// need to stuff websocket events into a channel.
newMessages := make(chan string)
go func() {
var msg string
for {
err := websocket.Message.Receive(ws, &msg)
if err != nil {
close(newMessages)
return
}
newMessages <- msg
}
}()
// Now listen for new events from either the websocket or the chatroom.
for {
select {
case event := <-subscription.New:
if websocket.JSON.Send(ws, &event) != nil {
// They disconnected.
return nil
}
case msg, ok := <-newMessages:
// If the channel is closed, they disconnected.
if !ok {
return nil
}
// Otherwise, say something.
chatroom.Say(user, msg)
}
}
return nil
}
参看资料:
http://www.cnblogs.com/ztiandan/archive/2013/01/23/2864872.html
http://robfig.github.com/revel/samples/chat.html
HTTP长连接
http://www.blogjava.net/xjacker/articles/334709.html
Comet:基于 HTTP 长连接的“服务器推”技术
http://www.ibm.com/developerworks/cn/web/wa-lo-comet/
android 客户端怎么实现长连接和短连接?
http://www.eoeandroid.com/thread-30241-3-1.html
Android/iOS Notification feature
http://blog.csdn.net/totogogo/article/details/7329542