zoukankan      html  css  js  c++  java
  • Go语言Revel框架 聊天室三种通讯方式分析

    三种机制的切换

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

    # 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)

    }

    长连接 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 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机制

    请求路由信息:

    # 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

  • 相关阅读:
    解决Ubuntu下pycharm无法输入中文的问题
    爬取www.mmjpg.com网站图片,你懂得哦!
    批量查询ip地址归属地
    Opencv源码编译
    使用阿里云安装python模块
    Ansible运维自动化
    Mha-Atlas-MySQL高可用
    SVN
    Tomcat
    DHCP
  • 原文地址:https://www.cnblogs.com/ghj1976/p/3002644.html
Copyright © 2011-2022 走看看