首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

反向 Ajax,第 1 部分 Comet 简介(2)

反向 Ajax,第 1 部分 Comet 简介(2)

Comet使用了轮询或是捎带的反向 Ajax 非常受限:其不具伸缩性,不提供低延迟通信(只要事件一到达服务器端,它们就以尽可能快的速度到达浏览器端)。          Comet 是一个 Web 应用模型,在该模型中,请求被发送到服务器端并保持一个很长的存活期,直到超时或是有服务器端事件发生。在该请求完成后,另一个长生存期的 Ajax 请求就被送去等待另一个服务器端事件。使用 Comet 的话,Web 服务器就可以在无需显式请求的情况下向客户端发送数据。
Comet 的一大优点是,每个客户端始终都有一个向服务器端打开的通信链路。服务器端可以通过在事件到来时立即提交(完成)响应来把事件推给客户端,或者它甚至可以累积再连续发送。因为请求长时间保持打开的状态,故服务器端需要特别的功能来处理所有的这些长生存期请求。图 3图 3 给出了一个例子。(本系列的第 2 部分会更详细地解释服务器端的约束条件。)
图 3. 图 3.使用 Comet 的反向 AjaxComet 的实现可以分成两类:使用流 (streaming) 的那些和使用长轮询 (long polling) 的那些。
使用 HTTP 流的 Comet在流 (streaming) 模式中,有一个持久连接会被打开。只会存在一个长生存期请求(图 3 中的 #1),因为每个到达服务器端的事件都会通过这同一连接来发送。因此,客户端需要有一种方法来把通过这同一连接发送过来的不同响应分隔开来。从技术上来讲,两种常见的流技术包括 Forever Iframe(或者 hidden  IFrame),或是被用来在 JavaScript 中创建 Ajax 请求的 XMLHttpRequest 对象的多部分 (multi-part) 特性。
Forever IframesForever Iframe(永存的 Iframe)技术涉及了一个置于页面中的隐藏 Iframe 标签,该标签的 src 属性指向返回服务器端事件的 servlet 路径。每次在事件到达时,servlet 写入并刷新一个新的 script 标签,该标签内部带有 JavaScript 代码,iframe 的内容被附加上这一 script 标签,标签中的内容就会得到执行。
  • 优点:实现简单,在所有支持 iframe 的浏览器上都可用。
  • 缺点:没有方法可用来实现可靠的错误处理或是跟踪连接的状态,因为所有的连接和数据都是由浏览器通过 HTML 标签来处理的,因此您没有办法知道连接何时在哪一端已被断开了。
多部分的 XMLHttpRequest第二种技术(更加可靠)是在 XMLHttpRequest 对象上使用某些浏览器(比如 Firefox)支持的 multi-part 标志。Ajax 请求被发送给服务器端并保持打开状态,每次有事件到来时,一个多部分的响应就会通过这同一连接来写入。清单 6清单 6 给出了一个例子。
清单 6. 清单 6 设置多部分流请求的 JavaScript 代码示例
1
2
3
4
5
6
7
8
9
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 流请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 来向挂起的多部分请求发送事件
1
2
3
4
5
6
7
8
9
10
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 视图照例,做法存在着一些优点和缺点。
  • 优点:只打开了一个持久连接,这就是节省了大部分带宽使用率的 Comet 技术。
  • 缺点:并非所有的浏览器都支持 multi-part 标志。某些被广泛使用的库,比如说用 Java 实现的 CometD,被报告在缓冲方面有问题。例如,一些数据块(多个部分)可能被缓冲,然后只有在连接完成或是缓冲区已满时才被发送,而这有可能会带来比预期要高的延迟。
返回列表