页面载入中,请稍候...

Browser和Server持续同步的几种方式(jQuery)

2013-03-05 22:18:03    作者:user    来源:会员投稿    评论:读取中    点击:读取中    [我要投稿]

长轮询(long-polling)方式长轮询是现在最为常用的方式,和流方式的区别就是服务器端在接到请求后挂起,有更新时返回连接即断掉,然后客户...

长轮询(long-polling)方式

long polling

长轮询是现在最为常用的方式,和流方式的区别就是服务器端在接到请求后挂起,有更新时返回连接即断掉,然后客户端再发起新的连接。于是Server端代码就简单好多,和上面的任务类似:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LongPollingHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def post(self):
        self.get_data(callback=self.on_finish)
             
    def get_data(self, callback):
        if self.request.connection.stream.closed():
            return
             
        num = random.randint(1, 100)
        tornado.ioloop.IOLoop.instance().add_timeout(
            time.time()+3,
            lambda: callback(num)
        ) # 间隔3秒调用回调函数
             
    def on_finish(self, data):
        self.write("Server says: %d" % data)
        self.finish() # 使用finish方法断开连接

Browser方面,我们封装成一个updater对象:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var updater = {
    poll: function(){
        $.ajax({url: "/longpolling"
                type: "POST"
                dataType: "text",
                success: updater.onSuccess,
                error: updater.onError});
    },
    onSuccess: function(data, dataStatus){
        try{
            $("p").append(data+"<BR>");
        }
        catch(e){
            updater.onError();
            return;
        }
        interval = window.setTimeout(updater.poll, 0);
    },
    onError: function(){
        console.log("Poll error;");
    }
};

要启动长轮询只要调用

 
1
updater.poll();

long polling

可以看到,长轮询与普通的轮询相比更有效率(只有数据更新时才返回数据),减少不必要的带宽的浪费;同时,长轮询又改进了streaming方式对于browser端判断并更新不足的问题。

WebSocket:未来方向

以上不管是Comet的何种方式,其实都只是单向通信,直到WebSocket的出现,才是B/S之间真正的全双工通信。不过目前WebSocket协议仍在开发中,目前Chrome和Safri浏览器默认支持WebSocket,而FF4和Opera出于安全考虑,默认关闭了WebSocket,IE则不支持(包括9),目前WebSocket协议最新的为“76号草案”。有兴趣可以关注http://dev.w3.org/html5/websockets/

在每次WebSocket发起后,B/S端进行握手,然后就可以实现通信,和socket通信原理是一样的。目前,tornado2.0版本也是实现了websocket的“76号草案”。详细可以参阅文档。我们还是只是在通信打开之后发送一堆随机数字,仅演示之用。

 
1
2
3
4
5
6
7
8
9
10
11
import tornado.websocket
 
class WebSocketHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        for i in xrange(10):
            num = random.randint(1, 100)
            self.write_message(str(num))
         
    def on_message(self, message):
        logging.info("getting message %s", message)
        self.write_message("You say:" + message)

客户端代码也很简单和直观:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var wsUpdater = {
    socket: null,
    start: function(){
        if ("WebSocket" in window) {
            wsUpdater.socket = new WebSocket("ws://localhost:8889/websocket");
        
        else {
            wsUpdater.socket = new MozWebSocket("ws://localhost:8889/websocket");
        }
        wsUpdater.socket.onmessage = function(event) {
            $("p").append(event.data+"<BR>");
        };
    }
};
wsUpdater.start();

WebSocket

————————————————————————————————————————————————————

附录:jQuery的JSONP

多亏了jQuery,提供了JSONP请求的方法:
 

$.getJSON('http://192.168.0.99/servlet3/getjsonpnew?callback=?')

指定需要回调的参数名callback,但其值使用一问号替代,作为占位符,jQuery会自动贴心的生成一个随机的调用函数名,服务器端的生成结果:
 

jQuery150832738454006945_1297761629067('2011-02-15 17:20:33 921');

我们直接在success函数中直接获取传入的值,无须担心那个看起来怪怪的回调函数名。JSONP的原理,在页面头部区动态产生一个JS文件引用(有人称为动态标签引用)。
 

<script src="http://192.168.0.99:8080/servlet3/getNextTime2?callback=jQuery150832738454006945_1297761629067&_=1297761631777"/>

虽然这一切看起来很好,很贴心,但也存在着不少问题:
 

  1. 无法直接显示指定回调函数名完美主义者可能无法忍受长长的、怪怪的函数名;当然了,可以定制的东西,总是让人很舒心
  2. 在长连接情况下,浏览器会一直显示正在加载中图示jQuery生成的JSONP JS文件在长连接情况下,会一直加载中,可能会影响到良好的用户体验,也会让前端UI体验师比较闹心。
很显然jQuery无法满足我们更高的实际要求。怎么办,请出jQuery-JSONP组件,基本上可以满足我们的要求。服务器端代码没有什么变化,有所变化的是客户端代码。
<html>
<head>
<title>Comet JSONP TEST</title>
    <meta http-equiv="X-UA-Compatible" content="IE=8" />
    <meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
<meta name="author" content="yongboy@gmail.com"/>
<meta name="keywords" content="servlet3, comet, ajax"/>
<meta name="description" content=""/>
<link type="text/css" rel="stylesheet" href="css/main.css"/>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.min.js"></script>
<script type="text/javascript" src="js/jquery.jsonp-2.1.4.js"></script>
<script type="text/javascript">
String.prototype.template=function(){
    var args=arguments;
    return this.replace(/\{(\d )\}/g, function(m, i){
        return args[i];
    });
}
var html = '<div class="logDiv">'
   '<div class="contentDiv">{0}</div>'
      '<div class="tipDiv">last date : {1}</div>'
      '<div class="clear">&nbsp;</div>'
      '</div>';

function showContent(json) {
 $("#showDiv").prepend(html.template(json.content, json.date));
}
var jsonpUrl = "http://192.168.0.99/servlet3/getjsonpnew";
function initJsonp(){
 $.jsonp({
       url: jsonpUrl,
       callbackParameter: 'callback',
       callback : 'yongboy', //eg:http://192.168.0.99/servlet3/getjsonpnew?callback=yongboy&_1297934909500=
       success: function(json) {
     if(json.state == 1){
    showContent(json);
   }
     initJsonp();
       },error: function(data) {
       }
 });
}
$(function (){
 // 为了兼容opera浏览器,设置为20秒延时时间(我本地测试若大于20s,可能出现连接中断问题)
 if($.browser.opera){
  jsonpUrl  = "?timeout=20000";
 }
 
 initJsonp();
});
</script>
</head>
<body style="margin: 0; overflow: hidden">
<div id="showDiv" class="inputStyle">loading ...</div>
</body>
</html>
嗯,jQuery-JSONP组件提供了更为灵活的jsonp参数配置:
  • 指定回调函数名
  • 提供错误处理函数,兼容opera
  • 在目前大部分浏览器中没有加载进度显示


经测试:
 

  1. IE
    页面不刷新,效果很满意。jQuery-JSONP源代码:
    if ( browser.msie ) {
          
          script.event = STR_ONCLICK;
          script.htmlFor = script.id;
          script[ STR_ONREADYSTATECHANGE ] = function() {
           /loaded|complete/.test( script.readyState ) && callback();
          };
          
         // All others: standard handlers
         } 
    
    使用了IE浏览器onreadystatechange加载事件等特性。
  2. Firefox
    script[ STR_ONERROR ] = script[ STR_ONLOAD ] = callback;
          
          browser.opera ?
           
           // Opera: onerror is not called, use synchronized script execution
           ( ( scriptAfter = $( STR_SCRIPT_TAG )[ 0 ] ).text = "jQuery('#" + script.id + "')[0]." + STR_ONERROR + "()" )
           
           // Firefox: set script as async to avoid blocking scripts (3.6+ only)
           : script[ STR_ASYNC ] = STR_ASYNC;
    
    看来我当前浏览器中3.6版本的火狐支持脚本文件async属性,非阻塞加载,再添加上onload,onerror事件支持,页面不刷新显示,也不难办到了。
  3. Chrome
    很不错,当前作为主力的Chrome浏览器也支持脚本文件的async属性,效果同3.6版本火狐。
  4. Opera
    情况很糟糕,在Windows Opera 11.01以及Ubuntu Opera 11.00下测试,长轮询时间若长于20秒,则会出现连接中断情况。样例中在Opear浏览器下设置长轮询过期时间为20s。
    浏览器会一直显示为正在加载中的状态。
    Opera浏览器中脚本加载不支持onerror事件,只能使用阻塞式的脚本加载机制用以处理有可能发生的异常。
    有关阻塞方式实现JSONP,延伸阅读:
    Comet (long polling) for all browsers using ScriptCommunicator
  5. Safari
    既然同为一个内核的谷歌浏览器也支持脚本文件的async属性,同样的异步加载也是水到渠成的了。
本文版权声明: 转载文章,有少量修改。如果本文或软件侵犯了您的版权,请告之网站管理员。

相关热词搜索:Browser   Server   持续  

上一篇:PHP 画图应用 验证码 柱状图
下一篇:开启pcntl_fork(开始php多线程)

分享到: 收藏