昨天说了长轮询(long-polling),我们把它比作是了一群人在排队上厕所。被这么多人轮着上,厕所都累了!今天要说的数据流就比较轻松,它没有那样长长的队伍,只有一人、一厕。而且这个人在厕所里一呆就是很久的,不会像长轮询那样一直进进出出。但是服务器是轻松了,开发人员就累了。
数据流通信,实际上即使和服务器建立一个HTTP连接,然后不断开,服务器只要一直通过那个连接发送数据就可以了。这个方法在兼容性上可不是那么容易搞定的。如果是用XHR对象做传输媒介,能完美兼容的只有Chrome和FireFox。Opera下XHR对象接收到数据不会产生事件,需要用一个计时器去判断是否收到数据。在IE下读取中的XHR对象根本就无法操作,所以在IE下我们必须找到其它方法来替代它。这就是使用IFRAME作为IE的传输媒介的方法,因为IFRAME在传输过程中,数据是可以被读出来的。接着又会遇上一个问题,如果使用IFRAME,浏览器会一直保持在加载状态。这就意味着,网页的图标会一直保持等待图标。这样当然不好,所以我们用了另一种手段让IFRAME加载的时候在IE中不显示加载状态。IE中支持HTMLFile对象,这个对象就相当于一个内存中的Document对象,它会解析文档。所以我们创建一个HTMLFile对象,在里面放置一个IFRAME来连接服务器。这样,各种浏览器就都支持了。现在我们来看代码,为了方便测试,服务器程序的代码我就写成了简单的每秒输出当期时间的程序。下面是代码
//设置文档类型,有些浏览器只解析HTML类型的文档
header('Content-Type:text/html; charset=utf-8');
//设置当前脚本为无超时状态
set_time_limit(0);
//先输出1KB的空格
//浏览器要接收一定量的数据之后才会开始解析
echo str_repeat(' ',1024);
flush();
//死循环
while(1){
//输出当前时间
echo date('Y-m-d H:i:s');
//输出一个分隔符
echo chr(1);
//发送到客户都
flush();
//等待1秒
sleep(1);
};
?>
接着是前端的代码,由于IE和其它浏览器的实现原理不同,所以代码会有点长,不过我都做了比较详细的注释,理解起来也不会太困难。
//获取浏览器信息
var isIE=navigator.userAgent.match(/MSIE (\d)/);
isIE=isIE?isIE[1]:undefined;
var isOpera=/Opera/.test(navigator.userAgent);
//设置参数
var url,splitchar;
url="test.php";
splitChar=String.fromCharCode(1);
//判断浏览器
if(isIE)
//如果是IE则使用这种方法
(function(){
var doc,ifr,itv,o,f;
//保存当前函数
f=arguments.callee;
//创建HTMLFile对象
doc=new ActiveXObject("HTMLFile");
//在HTMLFile中创建BODY
doc.write("
")
//创建IFRAME
ifr=doc.createElement("iframe");
//IFRAME的连接到服务器的地址
ifr.src=url;
//把IFRAME放入HTMLFile的BODY中
doc.body.appendChild(ifr);
//获取IFRAME中的document对象
o=ifr.contentWindow.document;
//创建计时器,循环从IFRAME中读取数据
itv=setInterval(function(){
//判断IFRAME的状态
switch(ifr.readyState){
case "interactive"://读取数据时
var i,s;
//把服务器返回的数据取出
s=o.body.innerHTML;
//分割数据,然后循环并操作数据
s=s.split(splitChar);
for(i in s)if(s[i])ReceivedData(s[i]);
//清空IFRAME接收到的数据
o.body.innerHTML="";
break;
case "complete"://服务器断开连接时
//关闭计时器
clearInterval(itv);
//调用当前函数重新连接服务器
f();
};
},1000);
})();
else
//如果非IE则使用这种方法
(function(){
var xhr,f,p;
//保存当前函数
f=arguments.callee;
//创建XHR对象
xhr=new XMLHttpRequest;
//设置数据游标
//由于服务器一开始会发送1KB的数据
//所以此处从1024开始
p=1024;
//添加爱XHR读取状态改变的事件
xhr.onreadystatechange=function(){
switch(xhr.readyState){
case 3://正在读取数据
var i,l,s;
//读取数据
s=xhr.response;
//获取数据长度
l=s.length;
//从游标位置开始获取数据,并用分割数据
s=s.slice(p,l-1).split(splitChar);
//循环并操作数据
for(i in s)if(s[i])ReceivedData(s[i]);
//更新游标位置
p=l;
//如果缓冲区占太多内存则断开连接
if(l>10485760)xhr.abort();
break;
case 4://与服务器断开
//重新连接
setTimeout(f,1000);
};
};
isOpera&&(function(){
xhr.onreadystatechange();
setTimeout(arguments.callee,1000);
})();
//设置连接参数
xhr.open("GET",url,true);
//连接服务器
xhr.send();
})();
//收到数据后执行的函数
function ReceivedData(e){
//把数据输出到BODY中
var div=document.createElement("div");
div.appendChild(document.createTextNode(e));
document.body.appendChild(div);
};
在IE中为了方法我直接使用了innerHTML来读取,简单的数据可以使用这种方式。不过如果需要输出更复杂的数据就需要自己组织好HTML了,或者把该转义的都转义了也行。其它浏览器中要注意的就是内存,因为XHR对象在连接状态下内存是不能释放的,所以如果需要优化应该往这方面优化。
数据流的方法是目前HTTP通信中最节省服务器资源的了。如果非要使用HTTP协议开发大型即时通信程序,就可以使用这个方法。当然我更推荐使用AS直接与服务器TCP通信的方法。
本文来源于广州网站建设公司与广州网站设计制作公司-广帆互动广州公司!