0
点赞
收藏
分享

微信扫一扫

Go语言Revel框架 聊天室三种通讯方式分析

三种机制的切换

首页相关的网页请求路由如下:

# 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

举报

相关推荐

0 条评论