本文同步自个人博客园:http://hustskyking.cnblogs.com
P.S: 各个平台中就 segmentFault 写博客体验最好了!javascript
web通讯,一个特别大的topic,涉及面也是很广的。因最近学习了 javascript 中一些 web 通讯知识,在这里总结下。文中应该会有理解错误或者表述不清晰的地方,还望斧正!php
浏览器做为 Web 应用的前台,自身的处理功能比较有限。浏览器的发展须要客户端升级软件,同时因为客户端浏览器软件的多样性,在某种意义上,也影响了浏览器新技术的推广。在 Web 应用中,浏览器的主要工做是发送请求、解析服务器返回的信息以不一样的风格显示。AJAX 是浏览器技术发展的成果,经过在浏览器端发送异步请求,提升了单用户操做的响应性。但 Web 本质上是一个多用户的系统,对任何用户来讲,能够认为服务器是另一个用户。现有 AJAX 技术的发展并不能解决在一个多用户的 Web 应用中,将更新的信息实时传送给客户端,从而用户可能在“过期”的信息下进行操做。而 AJAX 的应用又使后台数据更新更加频繁成为可能。html
随着互联网的发展,web 应用层出不穷,也不乏各类网站监控、即时报价、即时通信系统,为了让用户获得更好的体验,服务器须要频繁的向客户端推送信息。开发者通常会采用基于 AJAX 的长轮询方式或者基于 iframe 及 htmlfile 的流方式处理。固然有些程序须要在客户端安装各类插件( Java applet 或者 Flash )来支持性能比较良好的“推”信息。前端
短链接的操做步骤是:创建链接——数据传输——关闭链接...创建链接——数据传输——关闭链接
长链接的操做步骤是:创建链接——数据传输...(保持链接)...数据传输——关闭链接java
长链接与短链接的不一样主要在于client和server采起的关闭策略不一样。短链接在创建链接之后只进行一次数据传输就关闭链接,而长链接在创建链接之后会进行屡次数据数据传输直相当闭链接(长链接中关闭链接经过Connection:closed头部字段)。node
首先要搞清楚,xhr 的 readystate 各类状态。web
属性 | 描述 |
---|---|
onreadystatechange | 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。 |
readyState | 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。 0: 请求未初始化 1: 服务器链接已创建 2: 请求已接收 3: 请求处理中 4: 请求已完成,且响应已就绪 |
status | 200: "OK" 404: 未找到页面 |
轮询是一种“拉”取信息的工做模式。设置一个定时器,定时询问服务器是否有信息,每次创建链接传输数据以后,连接会关闭。ajax
前端实现:后端
var polling = function(url, type, data){ var xhr = new XMLHttpRequest(), type = type || "GET", data = data || null; xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { receive(xhr.responseText); xhr.onreadystatechange = null; } }; xhr.open(type, url, true); //IE的ActiveXObject("Microsoft.XMLHTTP")支持GET方法发送数据, //其它浏览器不支持,已测试验证 xhr.send(type == "GET" ? null : data); }; var timer = setInterval(function(){ polling(); }, 1000);
在轮询的过程当中,若是由于网络缘由,致使上一个 xhr 对象还没传输完毕,定时器已经开始了下一个询问,上一次的传输是否还会在队列中,这个问题我没去研究。若是感兴趣能够本身写一个ajax的请求管理队列。浏览器
长轮询其实也没啥特殊的地方,就是在xhr对象关闭链接的时候立刻又给他接上~ 看码:
var longPoll = function(type, url){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ // 状态为 4,数据传输完毕,从新链接 if(xhr.readyState == 4) { receive(xhr.responseText); xhr.onreadystatechange = null; longPoll(type, url); } }; xhr.open(type, url, true); xhr.send(); }
只要服务器断开链接,客户端立刻链接,不让他有一刻的休息时间,这就是长轮询。
数据流方式,在创建的链接断开以前,也就是 readystate 状态为 3 的时候接受数据,可是麻烦的事情也在这里,由于数据正在传输,你拿到的 xhr.response 可能就是半截数据,因此呢,最好定义一个数据传输的协议,好比前2个字节表示字符串的长度,而后你只获取这个长度的内容,接着改变游标的位置。
假如数据格式为: data splitChar data为数据内容,splitChar为数据结束标志(长度为1)。
那么传输的数据内容为 data splitChar data splitChar data splitChar...
var dataStream = function(type, url){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ // 状态为 3,数据接收中 if(xhr.readyState == 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]) deal(s[i]); p = l; //更新游标位置 } // 状态为 4,数据传输完毕,从新链接 if(xhr.readyState == 4) { xhr.onreadystatechange = null; dataStream(type, url); } }; xhr.open(type, url, true); xhr.send(); };
这个代码写的是存在问题的,当readystate为3的时候能够获取数据,可是这时获取的数据可能只是总体数据的一部分,那后半截就拿不到了。readystate在数据传输完毕以前是不会改变的,也就是说他并不会继续接受剩下的数据。咱们能够定时去监听readystate,这个下面的例子中能够看到。
这样的处理不算复杂,可是存在问题。上面的轮询和长轮询是全部浏览器都支持的,因此我就没有写兼容IE的代码,可是这里,低版本IE不容许在readystate为3的时候读取数据,因此咱们必须采用其余的方式来实现。
在ajax尚未进入web专题以前,咱们已经拥有了一个法宝,那就是iframe,利用iframe照样能够异步获取数据,对于低版本IE可使用iframe开接受数据流。
if(isIE){ var dataStream = function(url){ var ifr = document.createElement("iframe"), doc, timer; ifr.src = url; document.body.appendChild(ifr); doc = ifr.contentWindow.document; timer = setInterval(function(){ if(ifr.readyState == "interactive"){ // 处理数据,同上 } // 从新创建连接 if(ifr.readyState == "complete"){ clearInterval(timer); dataStream(url); } }, 16); }; };
定时去监听iframe的readystate的变化,从而获取数据流,不过,上面的处理方式仍是存在问题。数据流实现“服务器推”数据的原理是什么呢,简单点说,就是文档(数据)尚未加载完,这个时候浏览器的工做就是去服务器拿数据完成文档(数据)加载,咱们就是利用这点,给浏览器塞点东西过去~ 因此上述利用iframe的方式获取数据,会使浏览器一直处于加载状态,title上的那个圈圈一直在转动,鼠标的状态也是loading,这看着是至关不爽的。幸亏,IE提升了HTMLFile对象,这个对象就至关于一个内存中的Document对象,它会解析文档。因此咱们建立一个HTMLFile对象,在里面放置一个IFRAME来链接服务器。这样,各类浏览器就都支持了。
if(isIE){ var dataStream = function(url){ var doc = new ActiveXObject("HTMLFile"), ifr = doc.createElement("iframe"), timer, d; doc.write("<body/>"); ifr.src = url; doc.body.appendChild(ifr); d = ifr.contentWindow.document; timer = setInterval(function(){ if(d.readyState == "interactive"){ // 处理数据,同上 } // 从新创建连接 if(d.readyState == "complete"){ clearInterval(timer); dataStream(url); } }, 16); }; };
websocket是前端一个神器,ajax用了这么久了,相关技术也是很成熟,不过要实现个数据的拉取确实十分不易,从上面的代码中也看到了,各类兼容性问题,各类细节处理问题,自从有了websocket,哈哈,一口气上五楼...
var ws = new WebSocket("ws://www.example.com:8888"); ws.onopen = function(evt){}; ws.onmessage = function(evt){ deal(evt.data); }; ws.onclose = function(evt){}; //ws.close();
新建一个WebSocket实例,一切就OK了,ws://
是websocket的链接协议,8888为端口号码。onmessage中提供了data这个属性,至关方便
HTML5中提供的EventSource这玩意儿,这是无比简洁的服务器推送信息的接受函数。
new EventSource("test.php").onmessage=function(evt){ console.log(evt.data); };
简洁程度和websocket是同样的啦,只是这里有一个须要注意的地方,test.php输出的数据流应该是特殊的MIME类型,要求是"text/event-stream",若是不设置的话,你试试~ (直接抛出异常)
情非得已就别考虑这第六种方式了,虽然说兼容性最好,要是不懂as,出了点bug你也不会调试。
具体实现方法:在 HTML 页面中内嵌入一个使用了 XMLSocket 类的 Flash 程序。JavaScript 经过调用此 Flash 程序提供的套接口接口与服务器端的套接口进行通讯。JavaScript 在收到服务器端以 XML 格式传送的信息后能够很容易地控制 HTML 页面的内容显示。
这玩意儿原理和Flash相似,不过我不懂,就不细说了。
本文主要是总结Javascript的各类通信方式,后端配合node来处理,应该是挺给力的。
var conns = new Array(); var ws = require("websocket-server"); var server = ws.createServer(); server.addListener("connection", function(connection){ console.log("Connection request on Websocket-Server"); conns.push(connection); connection.addListener('message',function(msg){ console.log(msg); for(var i=0; i<conns.length; i++){ if(conns[i]!=connection){ conns[i].send(msg); } } }); }); server.listen(8888);
下面是一个php的测试demo。
header('Content-Type:text/html; charset=utf-8'); while(1){ echo date('Y-m-d H:i:s'); flush(); sleep(1); };