因公司做项目要求有一个面要实时获取最新数据。效果类似于实时聊天,或者淘宝支付页面(当手机上支付成功后)会自动跳转,还有就是微信手网页版(当用手机扫了页面上的二维码并确定后)也会自动跳转。
经过上百度和谷歌查询找到了一些有用的信息,现记录下来方便查阅。
comet相关资料:http://www.ibm.com/developerworks/cn/web/wa-lo-comet/
Comet是一种用于web的推送技术,能使服务器能实时地将更新的信息传送到客户端,而无须客户端发出请求,目前有两种实现方式:长轮询(long-polling)和iframe流(streaming)。
一、长轮询
长轮询就是客户端发起ajax请求,服务端会挂起该请求(通过循环),直到条件触发使服务器返回,在服务器返回前客户端一直处于pending,等待服务器返回。
长轮询可以减少客户端的无用请求,不过服务端的压力会大一些。另外可以采用websocket实现推送,不过兼容性会差些,有些浏览器不支持websocket。
服务端代码:
4 |
$filename = './phplog.com/data.txt' ; |
6 |
if (! empty ( $_GET [ 'm' ])) { |
7 |
file_put_contents ( $filename , $_GET [ 'm' ]); |
10 |
$old = filemtime ( $filename ); |
11 |
$cur = filemtime ( $filename ); |
12 |
while ( $cur <= $old ) { |
15 |
$cur = filemtime ( $filename ); |
17 |
echo file_get_contents ( $filename ) |
客户端代码:
5 |
< script src = "../phplog.com/jquery-1.9.0.min.js" ></ script > |
9 |
< button id = "click1" >click1</ button > |
13 |
$("#click1").click(function(){ |
18 |
beforeSend:function(){ |
24 |
data:"username=eddy&password=123456", |
25 |
success:function(data){ |
点击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进行刷新。
后台代码:
2 |
header( "Cache-Control: no-cache, must-revalidate" ); |
3 |
header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); |
9 |
<meta http-equiv= "Content-Type" content= "text/html; charset=utf-8" /> |
12 |
<script type= "text/javascript" > |
14 |
var is_khtml = navigator.appName.match( "Konqueror" ) || navigator.appVersion.match( "KHTML" ); |
17 |
var prototypejs = document.createElement( 'script' ); |
18 |
prototypejs.setAttribute( 'type' , 'text/javascript' ); |
19 |
prototypejs.setAttribute( 'src' , 'prototype.js' ); |
20 |
var head = document.getElementsByTagName( 'head' ); |
21 |
head[0].appendChild(prototypejs); |
24 |
var comet = window.parent.comet; |
28 |
echo '<script type="text/javascript">' ; |
29 |
echo 'comet.printServerTime(' .time(). ');' ; |
前端代码:
5 |
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" /> |
6 |
< script type = "text/javascript" > |
11 |
initialize: function() { |
14 |
comet.connection = new ActiveXObject("htmlfile"); |
15 |
comet.connection.open(); |
16 |
comet.connection.write("< html >"); |
17 |
comet.connection.write("< script >document.domain = '"+document.domain+"'"); |
18 |
comet.connection.write("</ html >"); |
19 |
comet.connection.close(); |
20 |
comet.iframediv = comet.connection.createElement("div"); |
21 |
comet.connection.appendChild(comet.iframediv); |
22 |
comet.connection.parentWindow.comet = comet; |
23 |
comet.iframediv.innerHTML = "< iframe id = 'comet_iframe' name = 'comet_iframe' src = './backend.php' ></ iframe >"; |
24 |
var comet_iframe = comet.connection.getElementById("comet_iframe"); |
25 |
comet.timer = window.setInterval(function(){ |
26 |
if(comet_iframe.readyState === "complete"){ |
27 |
comet_iframe.src = comet_iframe.src; |
31 |
comet.connection = document.createElement('iframe'); |
32 |
comet.connection.onload = function(){ |
33 |
comet.connection.src = comet.connection.src; |
35 |
comet.connection.setAttribute('id','comet_iframe'); |
36 |
comet.connection.setAttribute('src','./backend.php'); |
37 |
comet.connection.style.display = "none"; |
38 |
document.body.appendChild(comet.connection); |
41 |
// this function will be called from backend.php |
42 |
printServerTime: function (time) { |
43 |
document.getElementById('content').innerHTML = time; |
45 |
onUnload: function() { |
46 |
if (comet.connection) { |
47 |
comet.connection = false; // release the iframe to prevent problems with IE when reloading the page |
51 |
window.onload = function(){ |
54 |
window.onbeforeunload = function(){ |
60 |
< div id = "content" >The server time will be shown here</ div > |