Comet技术详解:基于HTTP长链接的Web端实时通讯技术

前言

通常来讲,Web端即时通信技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通信方案大体有4种:传统Ajax短轮询、Comet技术、WebSocket技术、SSE(Server-sent Events)。

关于这4种技术方式的优缺点,请参考《Web端即时通信技术盘点:短轮询、Comet、Websocket、SSE》。本文将专门讲解Comet技术。(本文同步发布于:http://www.52im.net/thread-334-1-1.htmljavascript

学习交流

- 即时通信开发交流群: 215891622 [推荐]php

- 更多即时通信技术资料:http://www.52im.net/forum.php?mod=collection&op=allhtml

概述

本文将介绍如何在现有的技术基础上选择合适的方案开发一个“服务器推”(Comet技术)的应用,最优的方案仍是取决于应用需求的自己。相对于传统的 Web 应用, 开发 Comet 应用具备必定的挑战性。

在WebSocket技术没有彻底解决浏览器兼容问题前,“服务器推”(Comet技术)存在普遍的应用需求,需求推进技术的发展,Comet 技术在Web端即时通信的方案里几乎不可或缺。java

“服务器推”(Comet技术)的应用范围

传统模式的 Web 系统以客户端发出请求、服务器端响应的方式工做。这种方式并不能知足不少现实应用的需求,譬如:

web

  • 监控系统:后台硬件热插拔、LED、温度、电压发生变化;
  • 即时通讯系统:其它用户登陆、发送信息;
  • 即时报价系统:后台数据库内容发生变化。


这些应用都须要服务器能实时地将更新的信息传送到客户端,而无须客户端发出请求。“服务器推”技术在现实应用中有一些解决方案,本文将这些解决方案分为两类:一类须要在浏览器端安装插件,基于套接口传送信息,或是使用 RMI、CORBA 进行远程调用;而另外一类则无须浏览器安装任何插件、基于 HTTP 长链接。

将“服务器推”应用在 Web 程序中,首先考虑的是如何在功能有限的浏览器端接收、处理信息:

数据库

  • 客户端如何接收、处理信息,是否须要使用套接口或是使用远程调用。客户端呈现给用户的是 HTML 页面仍是 Java applet 或 Flash 窗口。若是使用套接口和远程调用,怎么和 JavaScript 结合修改 HTML 的显示。
  • 客户与服务器端通讯的信息格式,采起怎样的出错处理机制。
  • 客户端是否须要支持不一样类型的浏览器如 IE、Firefox,是否须要同时支持 Windows 和 Linux 平台。

来看看更传统的基于客户端套接口的“服务器推”技术

1)Flash XMLSocket

若是 Web 应用的用户接受应用只有在安装了 Flash 播放器才能正常运行, 那么使用 Flash 的 XMLSocket 也是一个可行的方案。

这种方案实现的基础是:

浏览器

  • Flash 提供了 XMLSocket 类。
  • JavaScript 和 Flash 的紧密结合:在 JavaScript 能够直接调用 Flash 程序提供的接口。


具体实现方法:在 HTML 页面中内嵌入一个使用了 XMLSocket 类的 Flash 程序。JavaScript 经过调用此 Flash 程序提供的套接口接口与服务器端的套接口进行通讯。JavaScript 在收到服务器端以 XML 格式传送的信息后能够很容易地控制 HTML 页面的内容显示。

关于如何去构建充当了 JavaScript 与 Flash XMLSocket 桥梁的 Flash 程序,以及如何在 JavaScript 里调用 Flash 提供的接口,咱们能够参考 AFLAX(Asynchronous Flash and XML)项目提供的 Socket Demo 以及 SocketJS(请参见 参考资源)。

Javascript 与 Flash 的紧密结合,极大加强了客户端的处理能力。从 Flash 播放器 V7.0.19 开始,已经取消了 XMLSocket 的端口必须大于 1023 的限制。Linux 平台也支持 Flash XMLSocket 方案。但此方案的缺点在于:

服务器

  • 客户端必须安装 Flash 播放器;
  • 由于 XMLSocket 没有 HTTP 隧道功能,XMLSocket 类不能自动穿过防火墙;
  • 由于是使用套接口,须要设置一个通讯端口,防火墙、代理服务器也可能对非 HTTP 通道端口进行限制。


不过这种方案在一些网络聊天室,网络互动游戏中已获得普遍使用。

websocket

2)Java Applet 套接口

在客户端使用 Java Applet,经过 java.net.Socket 或 java.net.DatagramSocket 或 java.net.MulticastSocket 创建与服务器端的套接口链接,从而实现“服务器推”。

这种方案最大的不足在于 Java applet 在收到服务器端返回的信息后,没法经过 JavaScript 去更新 HTML 页面的内容。网络

基于 HTTP 长链接的“服务器推”技术:Comet技术

1)Comet 简介

浏览器做为 Web 应用的前台,自身的处理功能比较有限。浏览器的发展须要客户端升级软件,同时因为客户端浏览器软件的多样性,在某种意义上,也影响了浏览器新技术的推广。在 Web 应用中,浏览器的主要工做是发送请求、解析服务器返回的信息以不一样的风格显示。AJAX 是浏览器技术发展的成果,经过在浏览器端发送异步请求,提升了单用户操做的响应性。但 Web 本质上是一个多用户的系统,对任何用户来讲,能够认为服务器是另一个用户。现有 AJAX 技术的发展并不能解决在一个多用户的 Web 应用中,将更新的信息实时传送给客户端,从而用户可能在“过期”的信息下进行操做。而 AJAX 的应用又使后台数据更新更加频繁成为可能。

图 1. 传统的 Web 应用模型与基于 AJAX 的模型之比较:
Comet技术详解:基于HTTP长链接的Web端实时通讯技术_1.jpg 

“服务器推”是一种很早就存在的技术,之前在实现上主要是经过客户端的套接口,或是服务器端的远程调用。由于浏览器技术的发展比较缓慢,没有为“服务器推”的实现提供很好的支持,在纯浏览器的应用中很难有一个完善的方案去实现“服务器推”并用于商业程序。最近几年,由于 AJAX 技术的普及,以及把 IFrame 嵌在“htmlfile“的 ActiveX 组件中能够解决 IE 的加载显示问题,一些受欢迎的应用如 meebo,gmail+gtalk 在实现中使用了这些新技术;同时“服务器推”在现实应用中确实存在不少需求。由于这些缘由,基于纯浏览器的“服务器推”技术开始受到较多关注,Alex Russell(Dojo Toolkit 的项目 Lead)称这种基于 HTTP 长链接、无须在浏览器端安装插件的“服务器推”技术为“Comet”。目前已经出现了一些成熟的 Comet 应用以及各类开源框架;一些 Web 服务器如 Jetty 也在为支持大量并发的长链接进行了不少改进。关于 Comet 技术最新的发展情况请参考关于 Comet 的 wiki

下面将介绍两种 Comet 应用的实现模型。

2)Comet技术实现模型1:基于 AJAX 的长轮询(long-polling)方式

如 图 1 所示,AJAX 的出现使得 JavaScript 能够调用 XMLHttpRequest 对象发出 HTTP 请求,JavaScript 响应处理函数根据服务器返回的信息对 HTML 页面的显示进行更新。使用 AJAX 实现“服务器推”与传统的 AJAX 应用不一样之处在于:

  • 服务器端会阻塞请求直到有数据传递或超时才返回。
  • 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,从新创建链接。
  • 当客户端处理接收的数据、从新创建链接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端从新创建链接,客户端会一次把当前服务器端全部的信息取回。


图 2. 基于长轮询的服务器推模型:
Comet技术详解:基于HTTP长链接的Web端实时通讯技术_2.jpg 

一些应用及示例如 “Meebo”, “Pushlet Chat” 都采用了这种长轮询的方式。相对于“轮询”(poll),这种长轮询方式也能够称为“拉”(pull)。由于这种方案基于 AJAX,具备如下一些优势:请求异步发出;无须安装插件;IE、Mozilla FireFox 都支持 AJAX。

在这种长轮询方式下,客户端是在 XMLHttpRequest 的 readystate 为 4(即数据传输结束)时调用回调函数,进行信息处理。当 readystate 为 4 时,数据传输结束,链接已经关闭。Mozilla Firefox 提供了对 Streaming AJAX 的支持, 即 readystate 为 3 时(数据仍在传输中),客户端能够读取数据,从而无须关闭链接,就能读取处理服务器端返回的信息。IE 在 readystate 为 3 时,不能读取服务器返回的数据,目前 IE 不支持基于 Streaming AJAX。

3)Comet技术实现模型2:基于 Iframe 及 htmlfile 的流(streaming)方式

iframe 是很早就存在的一种 HTML 标记, 经过在 HTML 页面里嵌入一个隐蔵帧,而后将这个隐蔵帧的 SRC 属性设为对一个长链接的请求,服务器端就能源源不断地往客户端输入数据。

图 3. 基于流方式的服务器推模型:
Comet技术详解:基于HTTP长链接的Web端实时通讯技术_3.jpg 

上节提到的 AJAX 方案是在 JavaScript 里处理 XMLHttpRequest 从服务器取回的数据,而后 Javascript 能够很方便的去控制 HTML 页面的显示。一样的思路用在 iframe 方案的客户端,iframe 服务器端并不返回直接显示在页面的数据,而是返回对客户端 Javascript 函数的调用,如“<script type="text/javascript">js_func(“data from server ”)</script>”。服务器端将返回的数据做为客户端 JavaScript 函数的参数传递;客户端浏览器的 Javascript 引擎在收到服务器返回的 JavaScript 调用时就会去执行代码。

从 图 3 能够看到,每次数据传送不会关闭链接,链接只会在通讯出现错误时,或是链接重建时关闭(一些防火墙常被设置为丢弃过长的链接, 服务器端能够设置一个超时时间, 超时后通知客户端从新创建链接,并关闭原来的链接)。

使用 iframe 请求一个长链接有一个很明显的不足之处:IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成,并且 IE 上方的图标会不停的转动,表示加载正在进行。Google 的天才们使用一个称为“htmlfile”的 ActiveX 解决了在 IE 中的加载显示问题,并将这种方法用到了 gmail+gtalk 产品中。Alex Russell 在 “What else is burried down in the depth's of Google's amazing JavaScript?”文章中介绍了这种方法。Zeitoun 网站提供的 comet-iframe.tar.gz,封装了一个基于 iframe 和 htmlfile 的 JavaScript comet 对象,支持 IE、Mozilla Firefox 浏览器,能够做为参考。

使用 Comet 模型开发本身的应用

上面介绍了两种基于 HTTP 长链接的“服务器推”架构,更多描述了客户端处理长链接的技术。对于一个实际的应用而言,系统的稳定性和性能是很是重要的。将 HTTP 长链接用于实际应用,不少细节须要考虑。

1)不要在同一客户端同时使用超过两个的 HTTP 长链接

咱们使用 IE 下载文件时会有这样的体验,从同一个 Web 服务器下载文件,最多只能有两个文件同时被下载。第三个文件的下载会被阻塞,直到前面下载的文件下载完毕。这是由于 HTTP 1.1 规范中规定,客户端不该该与服务器端创建超过两个的 HTTP 链接, 新的链接会被阻塞。而 IE 在实现中严格遵照了这种规定。

HTTP 1.1 对两个长链接的限制,会对使用了长链接的 Web 应用带来以下现象:在客户端若是打开超过两个的 IE 窗口去访问同一个使用了长链接的 Web 服务器,第三个 IE 窗口的 HTTP 请求被前两个窗口的长链接阻塞。

因此在开发长链接的应用时, 必须注意在使用了多个 frame 的页面中,不要为每一个 frame 的页面都创建一个 HTTP 长链接,这样会阻塞其它的 HTTP 请求,在设计上考虑让多个 frame 的更新共用一个长链接。

2)服务器端的性能和可扩展性

通常 Web 服务器会为每一个链接建立一个线程,若是在大型的商业应用中使用 Comet,服务器端须要维护大量并发的长链接。在这种应用背景下,服务器端须要考虑负载均衡和集群技术;或是在服务器端为长链接做一些改进。

应用和技术的发展老是带来新的需求,从而推进新技术的发展。HTTP 1.1 与 1.0 规范有一个很大的不一样:1.0 规范下服务器在处理完每一个 Get/Post 请求后会关闭套接口链接; 而 1.1 规范下服务器会保持这个链接,在处理两个请求的间隔时间里,这个链接处于空闲状态。 Java 1.4 引入了支持异步 IO 的 java.nio 包。当链接处于空闲时,为这个链接分配的线程资源会返还到线程池,能够供新的链接使用;当原来处于空闲的链接的客户发出新的请求,会从线程池里分配一个线程资源处理这个请求。 这种技术在链接处于空闲的机率较高、并发链接数目不少的场景下对于下降服务器的资源负载很是有效。

可是 AJAX 的应用使请求的出现变得频繁,而 Comet 则会长时间占用一个链接,上述的服务器模型在新的应用背景下会变得很是低效,线程池里有限的线程数甚至可能会阻塞新的链接。Jetty 6 Web 服务器针对 AJAX、Comet 应用的特色进行了不少创新的改进,请参考文章“AJAX,Comet and Jetty”。

3)控制信息与数据信息使用不一样的 HTTP 链接

使用长链接时,存在一个很常见的场景:客户端网页须要关闭,而服务器端还处在读取数据的堵塞状态,客户端须要及时通知服务器端关闭数据链接。服务器在收到关闭请求后首先要从读取数据的阻塞状态唤醒,而后释放为这个客户端分配的资源,再关闭链接。

因此在设计上,咱们须要使客户端的控制请求和数据请求使用不一样的 HTTP 链接,才能使控制请求不会被阻塞。

在实现上,若是是基于 iframe 流方式的长链接,客户端页面须要使用两个 iframe,一个是控制帧,用于往服务器端发送控制请求,控制请求能很快收到响应,不会被堵塞;一个是显示帧,用于往服务器端发送长链接请求。若是是基于 AJAX 的长轮询方式,客户端能够异步地发出一个 XMLHttpRequest 请求,通知服务器端关闭数据链接。

4)在客户和服务器之间保持“心跳”信息

在浏览器与服务器之间维持一个长链接会为通讯带来一些不肯定性:由于数据传输是随机的,客户端不知道什么时候服务器才有数据传送。服务器端须要确保当客户端再也不工做时,释放为这个客户端分配的资源,防止内存泄漏。所以须要一种机制使双方知道你们都在正常运行。在实现上:

  • 服务器端在阻塞读时会设置一个时限,超时后阻塞读调用会返回,同时发给客户端没有新数据到达的心跳信息。此时若是客户端已经关闭,服务器往通道写数据会出现异常,服务器端就会及时释放为这个客户端分配的资源。
  • 若是客户端使用的是基于 AJAX 的长轮询方式;服务器端返回数据、关闭链接后,通过某个时限没有收到客户端的再次请求,会认为客户端不能正常工做,会释放为这个客户端分配、维护的资源。
  • 当服务器处理信息出现异常状况,须要发送错误信息通知客户端,同时释放资源、关闭链接。

Comet开源工程推荐

Pushlet:
Pushlet 是一个开源的 Comet 框架,在设计上有不少值得借鉴的地方,对于开发轻量级的 Comet 应用颇有参考价值。使用了观察者模型。浏览器端提供了基于 AJAX 和 iframe 的 JavaScript 库,服务器端使用 Java Servlet。地址是:http://www.pushlets.com/?cm_mc_uid=72410021035714633836363&cm_mc_sid_50200000=1464236784

iComet:
iComet 是一个使用 C++ 语言开发的支持百万并发链接的 comet/push 服务器, 支持百万级并发链接, 内存占用少, 性能优越. 可用于移动 App 的 Push Server(消息推送服务器), 或者用于 Web Push(Web 服务器推). 用于 Web Push 时, 支持的浏览器和操做系统平台包括: Safari(iOS, Mac), Firefox/Chrome(Windows, Mac), IE6+。详细请参见:http://www.52im.net/thread-330-1-1.html

系列文章

Web端即时通信新手入门:
新手入门:详解Web端即时通信技术的原理

Web端即时通信技术盘点请参见:
Web端即时通信技术盘点:短轮询、Comet、Websocket、SSE

关于Ajax短轮询:
找这方面的资料没什么意义,除非忽悠客户,不然请考虑其它3种方案便可。

有关Comet技术的详细介绍请参见:
Comet技术详解:基于HTTP长链接的Web端实时通讯技术
WEB端即时通信:HTTP长链接、长轮询(long polling)详解
WEB端即时通信:不用WebSocket也同样能搞定消息的即时性
开源Comet服务器iComet:支持百万并发的Web端即时通信方案

有关WebSocket的详细介绍请参见:
WebSocket详解(一):初步认识WebSocket技术
WebSocket详解(二):技术原理、代码演示和应用案例
WebSocket详解(三):深刻WebSocket通讯协议细节
Socket.IO介绍:支持WebSocket、用于WEB端的即时通信的框架
socket.io和websocket 之间是什么关系?有什么区别?

有关SSE的详细介绍文章请参见:
SSE技术详解:一种全新的HTML5服务器推送事件技术

更多WEB端即时通信文章请见:
http://www.52im.net/forum.php?mod=collection&action=view&ctid=15

(本文同步发布于:http://www.52im.net/thread-334-1-1.html

相关文章
相关标签/搜索