`
张江兴
  • 浏览: 121310 次
  • 性别: Icon_minigender_1
  • 来自: 湖南
社区版块
存档分类
最新评论

反向 Ajax,Comet 简介

阅读更多

 

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
优点:后端程序编写比较容易。
缺点:请求中有大半是无用,浪费带宽和服务器资源。
实例:适于小型应用。

 


长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
优点:在无消息的情况下不会频繁的请求,耗费资源小。
缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。
实例:WebQQHi网页版、Facebook IM

 

 

 

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframesrc属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。
优点:消息即时到达,不发无用请求;管理起来也相对方便。
缺点:服务器维护一个长连接会增加开销。
实例:Gmail聊天

 

 

 

Flash Socket在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。
优点:实现真正的即时通信,而不是伪即时。
缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。
实例:网络互动游戏。

 

 

 

简介

 

Web 开发在过去的几年中有了很大的进展,我们已经远超了把静态网页链接在一起的做法,这种做法会引起浏览器的刷新,并且要等待页面的加载。现在需要的是能够通过 Web 来访问完全动态的应用。这些应用通常需要尽可能的快,提供近乎实时的组件。在这个分为 5 部分的新系列中,我们学习如何使用反向 Ajax (Reverse Ajax) 技术来开发事件驱动的 Web 应用。

 

在这第一篇文章中,我们要了解反向 Ajax、轮询 (polling)、流 (streaming)Comet 和长轮询 (long polling)。学习如何实现不同的反向 Ajax 通信技术,并探讨每种方法的优点和缺点。

 


 

Ajax、反向 Ajax WebSockets

 

异步的 JavaScript XML (Ajax),一种可通过 JavaScript 来访问的浏览器功能特性,其允许脚本向幕后的网站发送一个 HTTP 请求而又无需重新加载页面。 Ajax 的出现已经超过了十年,尽管其名字中包含了 XML,但您几乎可以在 Ajax 请求中传送任何的东西。最常使用的数据是 JSON,它与 JavaScript 语法非常接近且消耗更少的带宽。清单 1清单 1 给出了这样的一个例子,Ajax 请求通过某个地方的邮政编码来检索该地的名称。

 

清单 1. 清单 1 Ajax 请求示例

 

var url = 'http://www.geonames.org/postalCodeLookupJSON?postalcode='

    + $('#postalCode').val() + '&country='

    + $('#country').val() + '&callback=?';

$.getJSON(url, function(data) {

    $('#placeName').val(data.postalcodes[0].placeName);

});

 

反向 Ajax (Reverse Ajax) 本质上则是这样的一种概念:能够从服务器端向客户端发送数据。在一个标准的 HTTP Ajax 请求中,数据是发送给服务器端的,反向 Ajax 可以某些特定的方式来模拟发出一个 Ajax 请求,这些方式本文都会论及,这样的话,服务器就可以尽可能快地向客户端发送事件(低延迟通信)。

 

WebSocket 技术来自 HTML5,是一种最近才出现的技术,许多浏览器已经支持它(FirefoxGoogle ChromeSafari 等等)。WebSocket 启用双向的、全双工的通信信道,其通过某种被称为 WebSocket 握手的 HTTP 请求来打开连接,并用到了一些特殊的报头。连接保持在活动状态,您可以用 JavaScript 来写和接收数据,就像是正在用一个原始的 TCP 套接口一样。WebSocket 会在这一文章系列的第二部分中谈及。

 


 

反向 Ajax 技术

 

反向 Ajax 的目的是让服务器将信息推送到客户端。Ajax 请求默认情况下是无状态的,且只能从客户端向服务器端发出请求。您可以通过使用技术模拟服务器端和客户端之间的响应式通信来绕过这一限制。

 

HTTP 轮询和 JSONP 轮询

 

轮询 (Polling) 涉及了从客户端向服务器端发出请求以获取一些数据,这显然就是一个纯粹的 Ajax HTTP 请求。为了尽快地获得服务器端事件,轮询的间隔(两次请求相隔的时间)必须尽可能地小。但有这样的一个缺点存在:如果间隔减小的话,客户端浏览器就会发出更多的请求,这些请求中的许多都不会返回任何有用的数据,而这将会白白地浪费掉带宽和处理资源。

 

1 1 中的时间线说明了客户端发出了某些轮询请求,但没有信息返回这种情况,客户端必须要等到下一个轮询来获取两个服务器端接收到的事件。

 

1. 使用 HTTP 轮询的反向 Ajax

 

 

 

JSONP 轮询基本上与 HTTP 轮询一样。不同之处在于使用 JSONP 您可以发送跨域请求(请求不属于您所在的域)。清单1使用了 JSONP 来通过邮政编码获取地名。JSONP 请求通常可通过它的回调参数和返回内容识别出来,这些内容是可执行的 JavaScript 代码。

 

要在 JavaScript 中实现轮询,您可以使用setInterval来定期地发出 Ajax 请求,如清单 2清单 2 所示:

 

清单 2. 清单 2 JavaScript 轮询

 

setInterval(function() {

    $.getJSON('events', function(events) {

        console.log(events);

    });

}, 2000);

 

轮询演示给出了轮询方法所消耗的带宽,间隔很小,但可以看到有些请求并未返回事件,清单 3清单 3 给出了这一轮询示例的输出。

 

清单 3. 清单 3 轮询演示例子的输出

 

[client] checking for events...

[client] no event

[client] checking for events...

[client] 2 events

[event] At Sun Jun 05 15:17:14 EDT 2011

[event] At Sun Jun 05 15:17:14 EDT 2011

[client] checking for events...

[client] 1 events

[event] At Sun Jun 05 15:17:16 EDT 2011

 

JavaScript 实现的轮询的优点和缺点。

 

<!--[if !supportLists]-->·         <!--[endif]-->优点:很容易实现,不需要任何服务器端的特定功能,且在所有的浏览器上都能工作。

 

<!--[if !supportLists]-->·         <!--[endif]-->缺点:这种方法很少被用到,因为它是完全不具伸缩性的。试想一下,在 100 个客户端每个都发出 2 秒钟的轮询请求的情况下,所损失的带宽和资源数量,在这种情况下 30% 的请求没有返回数据。

 

Piggyback

 

捎带轮询 (piggyback polling) 是一种比轮询更加聪明的做法,因为它会删除掉所有非必需的请求(没有返回数据的那些)。不存在时间间隔,客户端在需要的时候向服务器端发送请求。不同之处在于响应的那部分上,响应被分成两个部分:对请求数据的响应和对服务器事件的响应,如果任何一部分有发生的话。图 2 2 给出了一个例子。

 

2. 使用了 piggyback 轮询的反向 Ajax

 

 

 

在实现 piggyback 技术时,通常针对服务器端的所有 Ajax 请求可能会返回一个混合的响应。

 

清单 4. 清单 4 piggyback 代码示例

 

$('#submit').click(function() {

    $.post('ajax', function(data) {

        var valid = data.formValid;

        // process validation results

        // then process the other part of the response (events)

        processEvents(data.events);

    });

});

 

清单 5清单 5 给出了一些 piggyback 输出。

 

清单 5. 清单 5 piggyback 输出示例

 

[client] checking for events...

[server] form valid ? true

[client] 4 events

[event] At Sun Jun 05 16:08:32 EDT 2011

[event] At Sun Jun 05 16:08:34 EDT 2011

[event] At Sun Jun 05 16:08:34 EDT 2011

[event] At Sun Jun 05 16:08:37 EDT 2011

 

您可以看到表单验证的结果和附加到响应上的事件。同样,这种方法也有着一些优点和缺点。

 

<!--[if !supportLists]-->·         <!--[endif]-->优点:没有不返回数据的请求,因为客户端对何时发送请求做了控制,对资源的消耗较少。该方法也是可用在所有的浏览器上,不需要服务器端的特殊功能。

 

<!--[if !supportLists]-->·         <!--[endif]-->缺点:当累积在服务器端的事件需要传送给客户端时,您却一点都不知道,因为这需要一个客户端行为来请求它们。

 


 

Comet

 

使用了轮询或是捎带的反向 Ajax 非常受限:其不具伸缩性,不提供低延迟通信(只要事件一到达服务器端,它们就以尽可能快的速度到达浏览器端)。 Comet是一个 Web 应用模型,在该模型中,请求被发送到服务器端并保持一个很长的存活期,直到超时或是有服务器端事件发生。在该请求完成后,另一个长生存期的 Ajax 请求就被送去等待另一个服务器端事件。使用 Comet 的话,Web 服务器就可以在无需显式请求的情况下向客户端发送数据。

 

Comet 的一大优点是,每个客户端始终都有一个向服务器端打开的通信链路。服务器端可以通过在事件到来时立即提交(完成)响应来把事件推给客户端,或者它甚至可以累积再连续发送。因为请求长时间保持打开的状态,故服务器端需要特别的功能来处理所有的这些长生存期请求。图 3 3 给出了一个例子。(本系列的第 2 部分会更详细地解释服务器端的约束条件。)

 

3. 3.使用 Comet 的反向 Ajax

 

 

 

Comet 的实现可以分成两类:使用流 (streaming) 的那些和使用长轮询 (long polling) 的那些。

 


 

使用 HTTP 流的 Comet

 

在流 (streaming) 模式中,有一个持久连接会被打开。只会存在一个长生存期请求( 3 3 中的 #1),因为每个到达服务器端的事件都会通过这同一连接来发送。因此,客户端需要有一种方法来把通过这同一连接发送过来的不同响应分隔开来。从技术上来讲,两种常见的流技术包括 Forever Iframe(或者 hidden IFrame),或是被用来在 JavaScript 中创建 Ajax 请求的XMLHttpRequest对象的多部分 (multi-part) 特性。

 

Forever Iframes

 

Forever Iframe(永存的 Iframe)技术涉及了一个置于页面中的隐藏 Iframe 标签,该标签的src属性指向返回服务器端事件的 servlet 路径。每次在事件到达时,servlet 写入并刷新一个新的 script 标签,该标签内部带有 JavaScript 代码,iframe 的内容被附加上这一 script 标签,标签中的内容就会得到执行。

 

<!--[if !supportLists]-->·         <!--[endif]-->优点:实现简单,在所有支持 iframe 的浏览器上都可用。

 

<!--[if !supportLists]-->·         <!--[endif]-->缺点:没有方法可用来实现可靠的错误处理或是跟踪连接的状态,因为所有的连接和数据都是由浏览器通过 HTML 标签来处理的,因此您没有办法知道连接何时在哪一端已被断开了。

 

多部分的XMLHttpRequest

 

第二种技术(更加可靠)是在XMLHttpRequest对象上使用某些浏览器(比如 Firefox)支持的 multi-part 标志。Ajax 请求被发送给服务器端并保持打开状态,每次有事件到来时,一个多部分的响应就会通过这同一连接来写入。清单 6清单 6 给出了一个例子。

 

清单 6. 清单 6 设置多部分流请求的 JavaScript 代码示例

 

var xhr = $.ajaxSettings.xhr();

xhr.multipart = true;

xhr.open('GET', 'ajax', true);

xhr.onreadystatechange = function() {

    if (xhr.readyState == 4) {

        processEvents($.parseJSON(xhr.responseText));

    }

};

xhr.send(null);

 

在服务器端,事情要稍加复杂一些。首先您必须要设置多部分请求,然后挂起连接。清单 7清单 7 展示了如何挂起一个 HTTP 流请求。(本系列的第 3 部分会更加详细地谈及这些 API。)

 

清单 7. 清单 7 使用 Servlet 3 API 来在 servlet 中挂起一个 HTTP 流请求

 

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

        throws ServletException, IOException {

    // 开始请求的挂起

    AsyncContext asyncContext = req.startAsync();

    asyncContext.setTimeout(0);

 

    // 给客户端发回多部分的分隔符

    resp.setContentType("multipart/x-mixed-replace;boundary=\""

        + boundary + "\"");

    resp.setHeader("Connection", "keep-alive");

    resp.getOutputStream().print("--" + boundary);

    resp.flushBuffer();

 

    // 把异步上下文放在列表中以备将来之用

    asyncContexts.offer(asyncContext);

}

 

现在,每次有事件发生时您都可以遍历所有的挂起连接并向它们写入数据,如清单 8清单 8 所示:

 

清单 8. 使用 Servlet 3 API 来向挂起的多部分请求发送事件

 

for (AsyncContext asyncContext : asyncContexts) {

    HttpServletResponse peer = (HttpServletResponse)

        asyncContext.getResponse();

    peer.getOutputStream().println("Content-Type: application/json");

    peer.getOutputStream().println();

    peer.getOutputStream().println(new JSONArray()

        .put("At " + new Date()).toString());

    peer.getOutputStream().println("--" + boundary);

    peer.flushBuffer();

}

 

本文可下载文件的 Comet-straming 文件夹中的部分说明了 HTTP 流,在运行例子并打开主页时,您会看到只要事件一到达服务器端,虽然不同步但它们几乎立刻会出现在页面上。而且,如果打开 Firebug 控制台的话,您就能看到只有一个 Ajax 请求是打开的。如果再往下看一些,您会看到 JSON 响应被附在 Response 选项卡中,如图 4 4 所示:

 

4. HTTP 流请求的 Firebug 视图

 

 

 

照例,做法存在着一些优点和缺点。

 

<!--[if !supportLists]-->·         <!--[endif]-->优点:只打开了一个持久连接,这就是节省了大部分带宽使用率的 Comet 技术。

 

<!--[if !supportLists]-->·         <!--[endif]-->缺点:并非所有的浏览器都支持 multi-part 标志。某些被广泛使用的库,比如说用 Java 实现的 CometD,被报告在缓冲方面有问题。例如,一些数据块(多个部分)可能被缓冲,然后只有在连接完成或是缓冲区已满时才被发送,而这有可能会带来比预期要高的延迟。

 


 

使用 HTTP 长轮询的 Comet

 

长轮询 (long polling) 模式涉及了打开连接的技术。连接由服务器端保持着打开的状态,只要一有事件发生,响应就会被提交,然后连接关闭。接下来。一个新的长轮询连接就会被正在等待新事件到达的客户端重新打开。

 

您可以使用 script 标签或是单纯的XMLHttpRequest对象来实现 HTTP 长轮询。

 

script 标签

 

正如 iframe 一样,其目标是把 script 标签附加到页面上以让脚本执行。服务器端则会:挂起连接直到有事件发生,接着把脚本内容发送回浏览器,然后重新打开另一个 script 标签来获取下一个事件。

 

<!--[if !supportLists]-->·         <!--[endif]-->优点:因为是基于 HTML 标签的,所有这一技术非常容易实现,且可跨域工作(默认情况下,XMLHttpRequest不允许向其他域或是子域发送请求)。

 

<!--[if !supportLists]-->·         <!--[endif]-->缺点:类似于 iframe 技术,错误处理缺失,您不能获得连接的状态或是有干涉连接的能力。

 


 

XMLHttpRequest长轮询

 

第二种,也是一种推荐的实现 Comet 的做法是打开一个到服务器端的 Ajax 请求然后等待响应。服务器端需要一些特定的功能来允许请求被挂起,只要一有事件发生,服务器端就会在挂起的请求中送回响应并关闭该请求,完全就像是您关闭了 servlet 响应的输出流。然后客户端就会使用这一响应并打开一个新的到服务器端的长生存期的 Ajax 请求,如清单 9清单 9 所示:

 

清单 9. 清单 9 设置长轮询请求的 JavaScript 代码示例

 

function long_polling() {

    $.getJSON('ajax', function(events) {

        processEvents(events);

        long_polling();

    });

}

 

long_polling();

 

在后端,代码也是使用 Servlet 3 API 来挂起请求,正如 HTTP 流的做法一样,但是您不需要所有的多部分处理代码。清单 10清单 10 给出了一个例子。

 

清单 10. 清单 10 挂起一个长轮询 Ajax 请求

 

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

        throws ServletException, IOException {

    AsyncContext asyncContext = req.startAsync();

    asyncContext.setTimeout(0);

    asyncContexts.offer(asyncContext);

}

 

在接收到事件时,只是取出所有的挂起请求并完成它们,如清单 11清单 11 所示:

 

清单 11. 清单 11 在有事件发生时完成长轮询 Ajax 请求

 

while (!asyncContexts.isEmpty()) {

    AsyncContext asyncContext = asyncContexts.poll();

    HttpServletResponse peer = (HttpServletResponse)

        asyncContext.getResponse();

    peer.getWriter().write(

        new JSONArray().put("At " + new Date()).toString());

    peer.setStatus(HttpServletResponse.SC_OK);

    peer.setContentType("application/json");

    asyncContext.complete();

}

 

<!--[if !supportLists]-->·         <!--[endif]-->优点:客户端很容易实现良好的错误处理系统和超时管理。这一可靠的技术还允许在与服务器端的连接之间有一个往返,即使连接是非持久的(当您的应用有许多的客户端时,这是一件好事)。它可用在所有的浏览器上;您只需要确保所用的XMLHttpRequest对象发送到了简单的 Ajax 请求就可以了。

 

<!--[if !supportLists]-->·         <!--[endif]-->缺点:相比于其他技术来说,不存在什么重要的缺点,像所有我们已经讨论过的技术一样,该方法依然依赖于无状态的 HTTP 连接,其要求服务器端有特殊的功能来临时挂起连接。

 


 

建议

 

因为所有现代的浏览器都支持跨域资源共享(Cross-Origin Resource ShareCORS)规范,该规范允许XHR执行跨域请求,因此基于脚本的和基于 iframe 的技术已成为了一种过时的需要。

 

为反向 Ajax 实现和使用 Comet 的最好方法是通过XMLHttpRequest对象,它提供了一个真正的连接句柄和错误处理。考虑到不是所有的浏览器都支持 multi-part 标志,且多部分流可能会遇到缓冲问题,因此建议您选择经由 HTTP 长轮询使用XMLHttpRequest对象(在服务器端挂起的一个简单的 Ajax 请求)的 Comet 模式,所有支持 Ajax 的浏览器也都支持该种做法。

 


 

结束语

 

本文提供的是反向 Ajax 技术的一个入门级介绍,文章探索了实现反向 Ajax 通信的不同方法,并说明了每种实现的优势和弊端。您的具体情况和应用需求将会影响到您对最合适方法的选择。不过一般来说,如果您想要在低延迟通信、超时和错误检测、简易性,以及所有浏览器和平台的良好支持这几方面有一个最好的折中的话,那就选择使用了 Ajax 长轮询请求的 Comet

 

 

 

分享到:
评论

相关推荐

    Comet,反向Ajax,直接就能跑

    dwr comet 反向ajax实力 直接抛 我打了一个包, 放到Tomcat,jetty下面就能直接跑了 很方便 还有注视 对新手 。。。。很好的

    Comet, 下一代反向AJAX(即服务器推送技术- Server-side push)

    Comet 有时也称反向 Ajax 或服务器端推技术(server-side push)。其思想很简单:将数据直接从服务器推到浏览器,而不必等到浏览器请求数据。听起来简单,但是如果熟悉 Web 应用程序,尤其是 HTTP 协议,那么您就会...

    反向Ajax 30分钟快速掌握

    本文将分两个部分讨论反向Ajax技术,包括:Comet和WebSocket。文章旨在演示如何实现以上两种技术手段,Struts2或SpringMVC中的应用并未涉及。此外,Servlet的配置也采用注解的方式,相关知识大家可以参考其它资料。 ...

    php开发客服系统(持久连接+轮询+反向ajax) - php严程序

    一:iframe + 服务器推技术comet(反向ajax,即服务器向浏览器推送数据) 二:ajax持久连接 + 长轮询 客服端采用第一种方式:iframe + 服务器推技术 思路: 1:新建comentbyiframe.php 该用文件使用while(true)一直连接...

    comet的demo

    一个简单的comet实现的例子程序,就是comet实现长链接,反向ajax实现

    Comet4j demo

    Comet4j demo,进行对反向ajax进行编辑demo,实现聊天的实现

    DWR学习资料

    DWR学习资料 :DWR 3.0 上传文件.txt DWR3.0反向Ajax示例.txt DWR3.0学习笔记.txt DWR3.0学习网址.txt dwr分页.doc DWR分页代码.doc DWR中文文档.doc DWR中文文档.pdf dwr做comet的完整实现.doc Spring整合DWR comet ...

    dwr实现的网页即时聊天

    使用dwr框架的服务器推技术也称为反向ajax或comet,将项目发布到ajax中输入http://localhost:8080/dwr即可聊天 我实现的时群聊

    基于DWR的webIM系统

    基于DWR的webIM系统,利用反向ajax(comet)技术和dwr框架实现了聊天室和点对点聊天的功能,项目运行起来后打开页面,输入用户名即可登录,登录后用户会显示在左侧用户框中,若想与用户私聊,在用户框中点击用户,在...

    征服RIA:基于JavaScript的Web客户端开发卷三

    在这一篇中,读者将看到JavaScript如何游刃有余地整合各种技术流派,包括Flash、Applet、Silverlight、ActiveX等,如何开发反向Ajax程序、Comet程序和具备离线能力的程序。  本书适合JavaScript初学者、从事...

    征服RIA:基于JavaScript的Web客户端开发卷二

    在这一篇中,读者将看到JavaScript如何游刃有余地整合各种技术流派,包括Flash、Applet、Silverlight、ActiveX等,如何开发反向Ajax程序、Comet程序和具备离线能力的程序。  本书适合JavaScript初学者、从事...

    flask-socketio实现WebSocket的方法

    不过,反向ajax的代价也很明显,只要客户端还和服务端要有信息交互,服务端就必须还维持客户端的这个请求,然后在合适的时候返回。当客户端一多,这么做的成本会比较大。 其他的后端推前端的技术还有类似于隐藏...

    orbited:一个Rails插件,可以更轻松地向您的Rails应用添加Orbited支持

    ,也称为反向Ajax和服务器推送(以及其他昵称),是一种无需轮询即可将数据推送给用户的方法。 Orbited是此方法的一种实现,该实现使用Python推送服务器,该服务器可以与许多消息传递协议(例如STOMP,IRC和XMPP)...

Global site tag (gtag.js) - Google Analytics