FED实验室 - 专注WEB端开发和用户体验

实时web第一弹:Comet服务器推技术

JAVASCRIPT 煦涵 4368℃ 0评论

Comet是一种服务器向页面推送数据的技术,Comet能让信息近乎实时的被推送到页面上。实现Comet有两种方式:

方式一:长轮询

1)轮询与长轮询
轮询:也称定时轮询,客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。

优点:后端程序编写比较容易。
缺点:请求中有大半是无用,浪费带宽和服务器资源。
适用范围:适于小型应用。

长轮询:是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态更新或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。

优点:在无消息的情况下不会频繁的请求。
缺点:服务器hold连接会消耗资源。
适用范围:WebQQ、Hi网页版、Facebook IM。

2)长连接与socket

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。

优点:消息即时到达,不发无用请求。
缺点:服务器维护一个长连接会增加开销。
适用范围:Gmail聊天

Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。

优点:实现真正的即时通信,而不是伪即时。
缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。
适用范围:网络互动游戏。

3)实例之基于PHP+ajax实现的长轮询

	<ul id="message"></ul>   
	<input type="button" value="Test" id="btn">
	Javascript:
	<script src="jquery.js"></script>
	<script type="text/javascript">
	$(function(){
		var $message = $("#message"),
			$btn     = $("#btn");
			setPost = function() {
				$.ajax({
					type:"post",
					dataType:"json",
					url:"comet.php",
					timeout:1200000,
					data:{
						time:80
					},
					success:function(data) {
						$message.append("<li>"+ data.message +"</li>");
						setPost();
					},
					error:function() {

					}
				});
			};
		//绑定事件
		$btn.on("click",function(){
			setPost();
		});    
	});   
	</script>

 

	PHP:
	<?php
	if(empty($_POST['time'])) exit();  
	//设置脚本最大执行时间,为0,无限制
	set_time_limit(0);
	$i=0;    
	while (true){ 
		//以指定的微妙数延迟程序执行,0.5秒    
	    usleep(500000);  

	    $i ++;    
	    $rand = rand(1,999);

	    //每隔time,提示无数据    
	    if($i % $_POST["time"] == 0){    
	        $arr = array('success'=>"0",'username'=>'Benjamin','url'=>'http://www.zuojj.com','message'=>date('Y-m-d H:i:s',time()).'--No Data');
		    echo json_encode($arr);    
		    exit();   
	    }

	    if($rand <= 15){
	    	//有数据返回给客户端并结束本次请求 
	        $arr = array('success'=>"1",'username'=>'Benjamin','url'=>'http://www.zuojj.com','message'=>date('Y-m-d H:i:s',time()).'--Hava Data');  
		    echo json_encode($arr);    
		    exit();   
	    }  
	}

 

方式二:HTTP流

1)HTTP流

不同于两种轮询的地方在于它在页面的这个生命周期内只使用一个HTTP连接,简单的说就是客户端向服务器端发送请求,然后服务器端周期性的向浏览器发送数据。
常用技术方案通常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方案来改进用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。

2)实现原理
FF、Chrome、Safari中可以通过监听readystatechange事件和readyState状态===3,通过XHR对象实现HTTP流,随着不断地从服务器接收数据,readyState的值会周期性的变为3,responseText中就会保存接收到的所有数据。

3)实例

<div id="stream"></div>
<input type="button" id="btn" value="测试">
<script type="text/javascript" src="../jquery.js"></script>
<script type="text/javascript">
var comet = {
  connection   : false,
  iframediv    : false,
  initialize: function() {
    if (navigator.appVersion.indexOf("MSIE") != -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' src='./backend.php'></iframe>";

    } else if (navigator.appVersion.indexOf("KHTML") != -1) {
      // for KHTML browsers
      comet.connection = document.createElement('iframe');
      comet.connection.setAttribute('id',     'comet_iframe');
      comet.connection.setAttribute('src',    './backend.php');
      with (comet.connection.style) {
        position   = "absolute";
        left       = top   = "-100px";
        height     = width = "1px";
        visibility = "hidden";
      }
      document.body.appendChild(comet.connection);
    } else {
      // For other browser (Firefox...)
      comet.connection = document.createElement('iframe');
      comet.connection.setAttribute('id','comet_iframe');
      with (comet.connection.style) {
        left       = top   = "-100px";
        height     = width = "1px";
        visibility = "hidden";
        display    = 'none';
      }
      comet.iframediv = document.createElement('iframe');
      comet.iframediv.setAttribute('src', './backend.php');
      comet.connection.appendChild(comet.iframediv);
      document.body.appendChild(comet.connection);

    }
  },

  // this function will be called from backend.php  
  printServerTime: function (time) {
    $('#stream').append(time);
  },

  onUnload: function() {
    if (comet.connection) {
      comet.connection = false; // release the iframe to prevent problems with IE when reloading the page
    }
  }
}
$(window).on( "load", comet.initialize);
$(window).on("unload", comet.onUnload);

</script>

 

//backend.php
<?php
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
flush();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP</title>
<meta name="description" content="">
<meta name="keywords" content="">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="" />
</head>
<body>
<script type="text/javascript">
    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>

参考链接:http://www.zeitoun.net/articles/comet_and_php/start

下面是「FED实验室」的微信公众号二维码,欢迎扫描关注:

FED实验室

行文不易,如有帮助,欢迎打赏!

赞赏支持or喜欢 (1)or分享 (0)
捐赠共勉
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址