最新消息:文章中包含代码时,请遵守代码高亮规范!

Comet推送的两种方式

HTTP Charlie 14876浏览 2评论

因公司做项目要求有一个面要实时获取最新数据。效果类似于实时聊天,或者淘宝支付页面(当手机上支付成功后)会自动跳转,还有就是微信手网页版(当用手机扫了页面上的二维码并确定后)也会自动跳转。

经过上百度和谷歌查询找到了一些有用的信息,现记录下来方便查阅。

comet相关资料: http://www.ibm.com/developerworks/cn/web/wa-lo-comet/

Comet是一种用于web的推送技术,能使服务器能实时地将更新的信息传送到客户端,而无须客户端发出请求,目前有两种实现方式:长轮询(long-polling)和iframe流(streaming)。

一、长轮询

长轮询就是客户端发起ajax请求,服务端会挂起该请求(通过循环),直到条件触发使服务器返回,在服务器返回前客户端一直处于pending,等待服务器返回。

长轮询可以减少客户端的无用请求,不过服务端的压力会大一些。另外可以采用websocket实现推送,不过兼容性会差些,有些浏览器不支持websocket。

服务端代码:

<?php
//server.php 服务端
set_time_limit(0);
$filename = './phplog.com/data.txt';
//模拟新消息到来
if (!empty($_GET['m'])) {
    file_put_contents($filename, $_GET['m']);
    exit(0);
}
$old = filemtime($filename);
$cur = filemtime($filename);
while ( $cur &lt;= $old) {
    usleep(500000);//休息500ms
    clearstatcache();
    $cur = filemtime($filename);
}
echo file_get_contents($filename)
 ?> 

客户端代码:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="../phplog.com/jquery-1.9.0.min.js"></script>
    <title>PHP日志</title>
</head>
<body>
    <button id="click1">click1</button>
    <script>
    var sending=false;
    $(function(){
        $("#click1").click(function(){
            $.ajax({
                type:"POST",
                url:"server.php",
                timeout:0,
                beforeSend:function(){
                    if (sending) {
                        return false;
                    }
                    sending = true;
                },
                data:"username=eddy&password=123456",
                success:function(data){
                    alert(data);
                },
                error:function(data){
                    ;
                },
                complete:function(){
                    sending=false;
                    $("#click1").click();
                }
            });
        });
    });
    </script>
</body>
</html>

点击click1按钮,发起请求,等待服务端返回(新消息到来)。此时可通过访问 /server.php?m=1234568798798000 模拟消息到达,消息到达服务端返回数据,客户端弹出消息内容。

二、iframe流

iframe 是很早就存在的一种 HTML 标记, 通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
通过iframe里的内容进行长时间的请求,当需要传输内容时通过调用父页面js方法来实现页面展示,以此达到comet所需要的效果。
使用iframe流的一个不足就是在IE、opera、chrome浏览器下,始终会显示下载没完成,并且鼠标及浏览器上的那个圈一直在转动,在IE678下,google的大牛们通过使用“htmlfile”解决了这个问题,IE9我试了,并不支持”htmlfile”,当然IE9那个圈已经不转了。但是opera、chrome不支持“htmlfile”,无法解决。
iframe的另一个问题就是请求的超时,我们需要判断当iframe超时时,对其刷新重新建立连接。在普通情况下,我们可以通过iframe的onload事件进行刷新,当iframe连接内容超时时就会触发onload事件。在“htmlfile”下iframe却不支持onload,但是能读取到iframe的readyState,当readyState为complete时即表示请求超时,所以我们可以通过setInterval来判断iframe的readyState进行刷新。

后台代码:

<?php
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
flush();
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>PHP日志</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<script type="text/javascript">
  // KHTML browser don't share javascripts between iframes
  var is_khtml = navigator.appName.match("Konqueror") || navigator.appVersion.match("KHTML");
  if (is_khtml)
  {
    var prototypejs = document.createElement('script');
    prototypejs.setAttribute('type','text/javascript');
    prototypejs.setAttribute('src','prototype.js');
    var head = document.getElementsByTagName('head');
    head[0].appendChild(prototypejs);
  }
  // load the comet object
  var comet = window.parent.comet;
</script>
<?php
while(1) {
  echo '<script type="text/javascript">';
  echo 'comet.printServerTime('.time().');';
  echo '</script>';
  flush(); // used to send the echoed data to the client
  sleep(1); // a little break to unload the server CPU
}
?>
</body>
</html>

前端代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>PHP日志</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
var comet = {
    connection : false,
    iframediv : false,
    timer : 0,
    initialize: function() {
        if (!-[1,]) {
            // For IE browsers
            comet.connection = new ActiveXObject("htmlfile");
            comet.connection.open();
            comet.connection.write("<html>");
            comet.connection.write("<script>document.domain = '"+document.domain+"'");
            comet.connection.write("</html>");
            comet.connection.close();
            comet.iframediv = comet.connection.createElement("div");
            comet.connection.appendChild(comet.iframediv);
            comet.connection.parentWindow.comet = comet;
            comet.iframediv.innerHTML = "<iframe id='comet_iframe' name='comet_iframe' src='./backend.php'></iframe>";
            var comet_iframe = comet.connection.getElementById("comet_iframe");
            comet.timer = window.setInterval(function(){
                if(comet_iframe.readyState === "complete"){
                    comet_iframe.src = comet_iframe.src;
                }
            },1000*65)
        }else{
            comet.connection = document.createElement('iframe');
            comet.connection.onload = function(){
                comet.connection.src = comet.connection.src;
            }
            comet.connection.setAttribute('id','comet_iframe');
            comet.connection.setAttribute('src','./backend.php');
            comet.connection.style.display = "none";
            document.body.appendChild(comet.connection);
        }
    },
    // this function will be called from backend.php  
    printServerTime: function (time) {
        document.getElementById('content').innerHTML = time;
    },
    onUnload: function() {
        if (comet.connection) {
            comet.connection = false; // release the iframe to prevent problems with IE when reloading the page
        }
    }
}
window.onload = function(){
    comet.initialize();
}
window.onbeforeunload = function(){
    comet.onUnload();
}
</script>
</head>
<body>
<div id="content">The server time will be shown here</div>
</body>
</html>

转载时请注明出处及相应链接,本文永久地址:http://blog.it985.com/7797.html


pay_weixin
pay_weixin
微信打赏
pay_weixin
支付宝打赏
感谢您对作者charlie的打赏,我们会更加努力!    如果您想成为作者,请点我

您必须 登录 才能发表评论!

网友最新评论 (2)

  1. 我发现GoEasy也可以实现这个功能,自己感觉GoEasy要比comet简单易懂些,GoEasy推送专注做web实时推送,服务稳定,中英文文档齐全,是个很不错的选择!官网:https://goeasy.io
    Emma2016-05-26 10:32
  2. 我喜欢代码简洁易读,服务稳定的推送服务,前段时间研究了一下goeasy,后台推送只需要两行代码, js前端推送也只需要3,4行,而且文档齐全,还提供了后台查询信息收发情况,所以我觉得GoEasy推送服务是个不错的选择。 快速入门:goeasy.io/www/started.jsp GoEasy web实时推送官网:https://goeasy.io 1. 引入goeasy.js 2. 客户端订阅, Var goeasy = new GoEasy({appkey:’your appkey’}); goeasy.subscribe(channel:”your channel”, onMessage:function(message){alert(‘received message’+ message.content)}) 3. 三种推送方式 Javascript: goeasy.publish({channel:’your channel’, message:’your publish msg’}); Java SDK: GoEasy goeasy = new GoEasy(“appkey”); goeasy.publish(“your channel”,”your msg”); RestAPI: https://goeasy.io/goeasy/publish 三步轻松实现web推送及接收。
    点点滴滴2016-05-26 17:12