反向Ajax
——服务器推送技术介紹
首先,一般web场景都是这样的...
我想换身衣服,给我些css...
给我些png图片 & font文件,我想多些点缀
来吧,JavaScript!快来检测的V8引擎的性能...
要更新点数据,我发了Ajax请求,快点回复...
面对这些请求,服务器都会很「土豪」的说:
只要我有,我都會滿足...
此时,服务器总是被动响应
可是,我们总会遭遇这样的需求...
- Web实时监控系统
- 如"树莓派"监控实时交易额
- 股价实时更新
- 即时通信服务
- Web端QQ
- 新数据推送
- 网页端的邮件系统
这样的场景有两个特点:
- 客户端与服务器交互时间间隔短
- 某些场景,需服务器主动联系客户端
这些需服务器主动通知客户端的需求该怎么解决呢?
- Adobe Flash Socket
- Microsoft Silverlight
- Java Applet
基于客户端socket
import flash.errors.*;
import flash.events.*;
import flash.net.Socket;
class CustomSocket extends Socket {
private var response:String;
public function CustomSocket(host:String = null, port:uint = 0) {
super();
configureListeners();
if (host && port) {
super.connect(host, port);
}
}
private function configureListeners():void {
addEventListener(Event.CLOSE, closeHandler);
addEventListener(Event.CONNECT, connectHandler);
addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
addEventListener(SecurityErrorEvent.SECURITY_ERROR,
securityErrorHandler);
addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
}
...
}
与脱离浏览器的客户端Socket编程完全相同
FlashSocket、Silverlight & Applet都是浏览器之外的东西
-
无法直接通过 JavaScript 更新 HTML 页面的内容
- 浏览器必须安装配置对应的插件
So,这些都不是最好的实现「服务器推送」方式
Ajax 轮询
最简单的两种方式:
- 浏览器端不断用Ajax请求判断(polling)
- 浏览器端和服务器端配合实现长轮询(long-polling)
Ajax polling
// 频繁向服务器发Ajax请求,查询是否有更新
setInterval(function(){
$.ajax({ url: '/ajax', success: function(data){
// Do stuff with message
} });
}, 100);
// 服务器接收请求后立马反馈结果
public function postAjax() {
if (isUpdate($JOSN_DATA)) {
echo $JOSN_DATA;
} else {
echo '{"msg": "no update..."}';
}
}
http://10.64.12.173:8099/(树莓派,每分钟查询一次)
Ajax Long-Polling
// 向服务器端发送Ajax请求,得到服务器的反馈后立马重新请求
// 递归实现
function load(){
$.ajax({ url: '/ajax', success: function(){
// do something with the data
}, complete: load, timeout: 20000 });
}
// 服务器接收请求后不会立马反馈结果,满足要求后才反馈
public function postAjax() {
while(isUpdate($JOSN_DATA)) {
echo $JOSN_DATA;
} // 不满足`isUpdate($JOSN_DATA)`,死循环等待
}
这样,不必要的Ajax请求少了许多...
但服务器负担依旧很重...
Comet框架
什么是comet框架?
Comet is a web application model in which a long-held HTTP request allows a web server to push data to a browser, without the browser explicitly requesting it.
来自wikipedia的解释:
- 基于HTTP长链接
- 不依赖额外的浏览器插件
两个特点:
实现思路
- 基于HTTP流
- XMLHttpRequest
- 隐藏iframe(htmlfile)
- 基于长链接Ajax
- Ajax long polling
- JSONP polling
// 实现HTTP流的方式
public static function postHTTPStream() {
while(true) {
// 更新数据
$nowData = update();
// 打印数据
echo $nowData;
flush();
sleep(100);
}
}
采用流实现的服务器端的形式
- 不会立马给出全部结果
- 始终保持一个HTTP链接
浏览器端通过Ajax捕捉流数据:
function createStreamingClient(url, progress, finished) {
var xhr = new XMLHttpRequest(),
received = 0;
xhr.open("post", url, true);
xhr.onreadystatechange = function() {
var result;
console.log(xhr.readyState);
if (xhr.readyState == 3) {
result = xhr.responseText.substring(received);
received += result.length;
progress(result);
} else if (xhr.readyState == 4) {
finished(xhr.responseText);
}
};
xhr.send(null);
return xhr;
}
- 监听`onreadystatechange`事件
- 检测readyState值是否为3
不同的Comet框架需解决的问题
-
兼容性
-
跨域
-
中断判断
实现思路优缺点分析:
- 直接通过XMLHttpRequest实现HTTP流,IE不兼容(新firefox等也不支持),不能跨域
- JSONP polling:能解决跨域问题,只是Ajax long polling的优化,对服务器负担依旧大
- Ajax long polling:不能解决跨域问题;服务器负担大
- 隐藏iframe(htmlfile)是个比较好的实现方式
通过在 HTML 页面里嵌入一个隐蔵iframe,然后将这个隐蔵iframe的 SRC 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
iframe 服务器端并不返回直接显示在页面的数据,而是返回对客户端 Javascript 函数的调用,如:
<script type="text/javascript">js_func(“data from server ”)</script>”
服务器端将返回的数据作为客户端 JavaScript 函数的参数传递;客户端浏览器的 Javascript 引擎在收到服务器返回的 JavaScript 调用时就会去执行代码。
使用iframe标签实现HTTP流:
- 没有跨域困扰
- 所有浏览器都兼容iframe
但是也存在一些细节问题
- IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成
- IE 上方的图标会不停的转动,表示加载正在进行
来自Google的处理方式:
使用一个称为"htmlfile"的 ActiveX 解决了在 IE 中的加载显示问题。
相关链接:What else is burried down in the depth’s of Google’s amazing JavaScript?
中断判断:服务器怎么知道浏览器还在请求中?
添加一个最大等待时间判断:
服务器在接收到请求后,阻塞而不立刻返回。如果有新的事件,或者达到超时时间,再响应这个请求;避免服务器的无限等待。
典型产品
新的「宠物」:WebSocket
让服务器和浏览器实现双向通信
Title Text
// 插件WebSocket链接
var ws = new WebSocket("ws://url", "websocket");
ws.onopen = function() {
console.log('open');
};
// 接收服务器端的数据
ws.onmessage = function(evt) {
console.log(evt.data);
};
// 向服务器发送客户端数据
ws.send('client data');
// 关闭链接
ws.onclose = function() {
console.log('close');
};
缺陷:
- 高端浏览器的普及度太低,不是都支持WebSocket
- 大部分的WEB服务默认不支持WebSocket协议,需要配置
优点:
- 不依赖插件,也能像客户端socket编程一样方便;
- 不需要要像Comet一样,牵扯到许多很`Hack`的技巧;
总结
-
基于客户端Socket编程
- 基于Comet 框架
- Ajax 轮询
- 长链接HTTP流
- 基于WebSocket
谢谢!
反向Ajax——服務器推送技術介紹
By Ivan Lyons
反向Ajax——服務器推送技術介紹
介紹幾種常用的「服務器推送」技術實現的方式。
- 2,896