首页 > 代码库 > Ajax、服务器推和双向通信

Ajax、服务器推和双向通信

本文由大丰哥参考自:

congdepeng博文http://congdepeng.iteye.com/blog/1169481

stackoverflow问题"What are Long-Polling, Websockets, Server-Sent Events (SSE) and Comet?"Steve Chambers的答案

gemantic博文http://gemantic.iteye.com/blog/1553385

李刚《疯狂HTML5/CSS3/Javascript讲义》第18

MOZILLA DEVELOPER NETWORK文档https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events?

小胡子哥博文http://www.cnblogs.com/hustskyking/p/3479336.html

感谢以上诸位前辈。

#什么是Ajax?

异步的JavaScript和XML(Asynchronous?JavaScript and XML)。浏览器允许JavaScript脚本向服务器发送一个HTTP请求而又无需重新加载页面。

这个功能主要由浏览器完成,浏览器提供这个功能给JavaScript脚本来调用,然后浏览器根据规则来回调脚本去处理响应。所以Ajax中其实浏览器是最大苦力。JavaScript可以换成其他脚本,交换数据也不一定是XML格式的,所以XML也是可有可无的。所以Ajax中除了A(Asynchronous)以外,其他几个字母都是浮云。

#浏览器是如何封装异步调用功能给JavaScript调用?

浏览器把这个功能封装在一个JavaScript对象 window.XMLHttpRequest 里面。这个对象就像一个代理,为JavaScript调用提供接口。

XMLHttpRequest提供了一些方法以及属性如下:

  • open(method,?url,?async):调用的参数配置
  • send([string]):将请求发送到服务器
  • onreadystatechange( ):由浏览器回调的方法,由开发人员编写
  • readyState:HTTP请求的状态
  • status:HTTP状态码

浏览器其实只是封装了一个Socket调用而已。XMLHttpRequest?作为封装这个?Socket?的?Proxy。

Socket的工作流程:

创建Socket对象-->建立连接-->发送数据-->接受数据-->处理接受到的数据-->关闭连接

Ajax的工作流程:

创建XMLHttpRequest对象-->设置连接的相关参数open(method,url,async)-->发送数据send([string])-->处理接受到的数据onreadystatechange( )

对比Socket和Ajax的工作流程,你会发现经过浏览器封装后的Socket调用更加简单,你不需要主动建立连接和关闭连接,只需要设置好相关的参数,同时写好如何处理接受到的数据的方法,那么关于Socket以及HTTP协议的细节问题就交给浏览器去做了。

Ajax调用的基本代码示例:

var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function(){
// xmlhttp.readyState
// xmlhttp.status
}
xmlhttp.open("GET", "/ajax/demo_get.php", true);
xmlhttp.send();

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);

Ajax发送的内容仍然是基于HTTP协议的请求。理论上Ajax发送一个请求和你在地址栏里面输入请求是一模一样的。服务器应该根本就不知道什么是Ajax,服务器只处理HTTP请求。

#什么是Reverse Ajax

先谈谈为什么要Reverse Ajax:

因为HTTP协议是无状态的,即请求完数据后就关闭了连接。如果服务器有新的数据,浏览器是无法知道的,必须主动去查询才能知道。又因为Ajax是基于HTTP的,所以Ajax请求在缺省情况下也是无状态的,且只能从客户端向服务器端发出请求。(可以通过使用技术模拟服务器端和客户端之间的响应式通信来绕过这一限制)。

为了尽快地获得服务器端事件,我们只能使用粗暴的HTTP轮询来完成任务。轮询的间隔(两次请求相隔的时间)必须尽可能地小。

  • 优点:很容易实现,不需要任何服务器端的特定功能,且在所有的浏览器上都能工作。
  • 缺点:如果间隔减小的话,客户端浏览器就会发出更多的请求,这些请求中的许多都不会返回任何有用的数据,而这将会白白地浪费掉带宽和处理资源。

用?firebug 调试一下 weibo.com 的网络请求可以发现,微博用的是轮询来实现消息提醒的,应该是用set timer隔个0.7分钟去服务器进行查询。和即时通信的web应用不同,微博提醒实时性要求不高,所以用轮询方式比较合理,没有必要用长连接。

?

有一个叫捎带轮询(piggyback polling)的实现方式是一种比轮询更加聪明的做法。它会删除掉所有非必需的请求(没有返回数据的请求)。不存在时间间隔,客户端在需要的时候向服务器端发送请求。不同之处在于响应的那部分上,响应被分成两个部分:对请求数据的响应和对服务器事件的响应,如果任何一部分有发生的话,在实现piggyback技术时,通常针对服务器端的所有Ajax请求可能会返回一个混合的响应。

  • 优点:没有不返回数据的请求。因为客户端对何时发送请求做了控制,对资源的消耗较少。该方法也可用在所有的浏览器上,不需要服务器端的特殊功能。
  • 缺点:当累积在服务器端的事件需要传送给客户端时,你却一点都不知道,因为这需要一个客户端行为来请求它们。

??

我们回到原点,为什么要关闭连接然后再不停的建立连接呢?为什么不建立一个时间非常长的连接呢?

因为以前的HTTP 1.0不支持长连接,所以我们不得已只能轮询!到了HTTP 1.1开始支持长连接,我们可以建立一个长连接来完成这个目的。下面介绍长连接。

#Comet

Comet 是一种?Web?应用程序架构。可以说,它不是一种技术,而是一种思想,只是这种思想采用了已有的技术去实现。在这种思想里,客户端(Client)不需要显式地向服务器端(Server)发出请求,Server会在其数据发生变化的时候主动将数据异步发送给Client,从而使Client能够及时更新数据并呈现给用户。它不同于传统的Web,也不同于当前流行的Ajax,这种思想非常架构思想非常适合event-driven(事件驱动)式的Web应用和对交互性及实时性要求很强的应用,比如股票交易,聊天室,Web IM,网游等。

?

最常见的有下面两种方式:

1. HTTP?长轮询(HTTP Long Polling):Javascript在处理完服务器返回的信息后再次发出请求,重新建立连接。不同于一般的Ajax,Javascript请求Server,无数据时Server不中断请求(still loading),在一定时间内获取到数据后,返回请求,又在获取数据后再次发出请求,由此轮询。需要注意的是请求的间隔时间以及每次请求的最长Loading时间。做个比喻,如果轮询是排队买东西,那么长轮询就是排队上厕所。买东西的话,丢下钱就可以拿东西走了,但是上厕所就不一样,有些人说不定便秘半个小时都出不来。如果只用轮询去做Web通信,那服务器就鸭梨山大了,它需要一直做接受和断开HTTP请求的操作。就像卖热销产品的店员就没有公共厕所管理员那么轻松。

?

  • 优点:异步请求;无需浏览器任何插件支持;采用Ajax技术,兼容性强;
  • 缺点:会产生大量的通信量,只能通过增加轮询的时间间隔来减轻Server的压力;

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();
}

只要服务器断开连接,客户端马上连接,不让有一刻的休息时间,这就是长轮询。

?

2. Iframe结合Htmlfile流(streaming)HTTP?流(HTTP Streaming):通过在页面上嵌入一个隐藏的Iframe,设置其src属性为一个长连接的请求,Server采用flush方式将数据作为前端Javascript函数的参数传递;

  • 优点:不会有很大的通信量,而且数据接收非常及时,并且无中断;
  • 缺点:会产生进度条的Loading状态并一直存在,用户使用体验很不好。在Google Talk中,通过Htmlfile Active解决了IE下的进度条显示问题;保持长期链接也非常耗服务器资源;

反向Ajax的目的是允许服务器端向客户端推送信息。这样如果服务端有数据更新,就可以及时推送给客户端。

#HTML5?Server-Sent Events

Server-Sent Events是一个从服务器到浏览器的单向推送,实际上是将Comet技术进行了标准化。Server-Sent Events规范"定义了API来打开一个HTTP连接,通过该连接能够获取从服务器推送的通知"。Server-Sent Events包含新的 Javascript 对象?EventSource 和新的 MIME 类型 text/event-stream,这个 MIME 类型定义了事件框架格式。

EventSource 代表的是接收事件的客户端的终点。客户端通过创建 EventSource 对象来打开一个 event stream。创建 EventSource 对象时,该对象接收一个事件来源的 URI 作为其构造函数的参数。当每次收到新的事件数据时 onmessage 事件处理器会被调用。


Server-Sent Events是基于HTTP streaming的。如上所述,响应会一直打开,当服务器端有事件发生的时候,事件会被写入响应中。

#HTML5 ?WebSocket

WebSocket 启用了双向的全双工通信信道,许多浏览器(Firefox、Google Chrome 和 Safari)都已对此做了支持。连接会保持在活动状态,可以使用 JavaScript 来写入和接收数据,就像是在使用一个原始的TCP套接口一样。WebSocket URL的起始输入是ws://或是wss://(在SSL上),从这个ws和wss上可以想到,WebSocket?已经不是建立在?HTTP?协议之上了!

  • 优点:功能强大、双向、低延迟,且易于处理错误。不像 Comet 长轮询那样有许多的连接。它的API 也很容易使用,无需另外的层就可以直接使用,而Comet 则需要一个很好的库来处理重连接、超时、Ajax请求、确认以及选择不同的传输(Ajax 长轮询和 jsonp 轮询)。
  • 缺点:是一个来自 HTML5 的新规范,还没有被所有的浏览器支持。没有请求作用域(request scope)(因为?WebSocket?是一个?TCP?套接口而不是一个?HTTP?请求),有作用域的请求服务,比如说 Hibernate 的 SessionInViewFilter,就不太容易使用。

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();

Ajax、服务器推和双向通信