三种机制的切换
首页相关的网页请求路由如下:
# Login
GET /
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.Flash.Error("Please choose a nick name and the demonstration type.")
return
}
switch
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
}
每隔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
5000)
scrollDown()
</script>
Refresh.Room 的处理逻辑:
使用一个订阅者把目前聊天室的所有信息都显示出来。离开这个处理过程就把订阅者注销。
func (c Refresh) Room(user string) revel.Result {
:=
defer
:=
for i, _ := range
if events[i].User ==
"you"
}
}
return
}
对 /refresh/room 发出P
ost请求是,则记录发出的消息,同时刷新页面。
func (c Refresh) Say(user, message string) revel.Result {
chatroom.Say(user, message)
return
}
离开聊天室时,标示离开,同时跳转页面。
func (c Refresh) Leave(user string) revel.Result {
chatroom.Leave(user)
return
}
长连接 comet
长连接的路由请求如下:
# 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
}
长连接的浏览器端核心逻辑如下,删除了一些跟核心逻辑无关的代码:
向服务器请求有没有新的消息,如果没有新的消息,则会一直等待服务器。如果有则请求完成消息,然后再次发出一个请求getMessages();
<script type="text/javascript">
var lastReceived = 0
var waitMessages ='/longpolling/room/messages?lastReceived='
// Retrieve new messages
var getMessages = function() {
$.ajax({
: waitMessages +
: function(events) {
function() {
this)
= this.Timestamp
})
getMessages()
},
: 'json'
});
}
getMessages();
// Display a message
var display = function(event) {
'#thread').append(tmpl('message_tmpl', {event:
'#thread').scrollTo('max')
}
</script>
服务器端处理逻辑则是借用了channel无法读出新的内容时,会一直等下去的技巧,代码如下:
func (c LongPolling) WaitMessages(lastReceived int) revel.Result {
:=
defer
// See if anything is new in the archive.
var
for _, event := range
if
append(events, event)
}
}
// If we found one, grand.
if len(events) > 0
return
}
// Else, wait for something new.
:= <-subscription.New
return
}
离开和发送消息的逻辑跟定时刷新的机制基本类似,就不再表述。
WebSocket机制
请求路由信息:
# 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:
'#thread').scrollTo('max')
}
// Message received on the socket
= 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.
:=
defer
chatroom.Join(user)
defer
// Send down the archive.
for _, event := range
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.
:= make(chan string)
go func() {
var msg string
for
:= websocket.Message.Receive(ws, &msg)
if err != nil
close(newMessages)
return
}
<-
}
}()
// 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
return nil
}
// Otherwise, say something.
chatroom.Say(user, msg)
}
}
return nil
}
参看资料:
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 客户端怎么实现长连接和短连接?
Android/iOS Notification feature
标签:聊天室,websocket,room,Refresh,Revel,user,Go,return,message From: https://blog.51cto.com/u_15588078/6768238