原文地址
本文内容即时通讯,在 Web 早先开发就有,那时最常见的实现手段是轮询(polling)。轮询是在某个时间间隔(如1秒),由浏览器向服务器发出 HTTP request,然后服务器返回最新的数据给客户端的浏览器。这种方式最明显的缺点,是浏览器需要不断的向服务器发出请求,即便都没有消息了,而且,HTTP request 的 header 很长,实际有用数据可能只是一个很小的值,无形中占用带宽。之后,出现了 Comet,但这种技术一定程度上只是模拟全双工通信,效率较低,并需要服务器有较好的支持。
(数据通信中,数据在线路上的传送方式可以分为单工通信、半双工通信和全双工通信三种,具体不解释,比如,遥控器是单工的,对讲机是半双工的,电话是全双工的。)
WebSocket protocol 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex),能更好的节省服务器资源和带宽并达到实时通讯。浏览器通过 Http 仅能实现单向通信,轮询也好,comet 也罢,都不是很理想;flash 中的 socket 和 xmlsocket 可以实现真正的双向通信。通过 flex ajax bridge,可以在 Javascript 中使用这两项功能。如果 Websocket 一旦在浏览器中得到实现,将取代上面两项技术,得到广泛的使用。
Websocket Demo早期的Web技术都是基于HTTP协议而发展起来的,而HTTP只是一个简单的基于请求 —— 响应操作的协议,所有的请求都是由客户端发起的。这套框架原本足以满足用户的需求,但在如今开发者所设计的web应用中,由客户端发起通信这种方式有着很大的制约。虽然人们提出了各种临时方案,但它们都是基于HTTP协议的,只是应用了轮询或长轮询技术(例如Comet)。Comet能够让负责处理请求的线程得到释放,以防止服务器资源耗尽。由于轮询这种机制并不可靠,因此在2007年时,有人提出了一种名为WebSocket的全双工(full- duplex)类型的通信方式。这项提议用了整整4年的时间才成为一个标准。但是,尽管它已成为一种标准,但它的使用率却相当有限。本文将为读者解释妨碍 WebSocket应用的两大原因,并且提出了一个设计框架,开发者可以使用这套框架快速地发挥WebSocket的潜能,并且极大地丰富应用的体验。
导致 WebSocket 使用率低下的第一个原因在于应用服务器与浏览器对其支持不足。但随着新一代应用服务器与浏览器的出现,这种状况得到了很大的改善。而第二个原因比起前一点来说其影响更大,亦即要充分利用WebSocket的全部潜能,必须对Web应用进行颠覆性的重新设计。而这个重新设计过程需要将基础的请求 —— 响应这一结构转变为更复杂的双向消息传递结构。应用程序的重新设计往往是一个开销很大的过程,而软件供应商很难从这一过程中看到任何显著的利益。
我们首先将对WebSocket做个简单的介绍,随后展示一种使用WebSocket重新构建应用程序的方法,最后通过一个简单的示例表现这一方法的各种要点。
WebSocket 简介WebSocket 是在 TCP/IP 协议之上创建的一种帧协议,客户端通过向服务器发送一种特殊的 HTTP 请求来启动 WebSocket。在最初的握手过程之后,客户端与服务器就能够自由地以异步方式互相进行帧的传送了。帧分为两种类型:即控制帧与数据帧。最小的控制帧仅有2比特的大小,客户端最小的数据帧为6比特,服务端最小的数据帧为2比特。数据帧既可以是文本型,也可以是二进制的。文本帧都经过了UTF-8的编码。帧可以实现分块,因此一个大数据集可以分解为多个帧。WebSocket不会为帧附加任何标识信息,因此不同类型的信息对应的帧不可混用。只有控制帧能够在处理一个大消息时的一系列中间帧中出现。在这些基础的帧之上,还可以定义更复杂的协议。比方说,一个帧能够带有校验和或是它的序列号等相关信息。
WebSocket APIWebSocket 并不限定于仅在某个特定的编程语言、系统或是操作系统中使用。多数主流的编程语言以及许多浏览器都已开始支持WebSocket 的编程。虽然在不同的平台与编程语言中存在着大量的标准,但本文仅关注JavaScript HTML5以及Java(J2EE)对WebSocket的支持。在浏览器这方面有两种实现标准,其最新版本分别为Hixie-76和HyBi-17(不久之后发展为IETF RFC 6455)。HyBi的实现相对更高级,并且得到了目前所有主流浏览器的支持。而在服务端方面,基于Java的实现则是目前最为流行的。早些时候在 Java上曾经出现过几种WebSocket的实现,它们之后已发展为JSR 356这种实现。JSR代表Java规范请求,对规范请求的说明有帮于让之后的各种实现保持一致性,并且易于使用。JSR也让开发者不必依赖于某个特定的实现。JSR 356与servlet规范是相互分离的,但它也允许开发者访问某些servlet对象。JSR 356的内容涵盖了WebSocket连接的客户端与服务端, 我们稍后的讨论将集中于配合浏览器端的JavaScript所实现的服务端。JSR 356目前属于J2EE 7的一部分,所有流行的开源Java应用服务器都支持它,包括Tomcat、Jetty、Glassfish以及TJWS等等。除此之外,在Java环境中还存在着大约20种各自独立的WebSocket服务端解决方案,其中有些方案也支持JSR 356。由于WebSocket是J2EE 7的一部分,因而在由Oracle与IBM所推出的商业应用服务器上同样也得到支持。
正如我之前所说,WebSocket是一种消息传递协议。它的API提供了各种在通信双方进行消息传递与接收的方法。这里并不存在经典的订阅者与发布者的关系。消息只有两种类型,即文本型与二进制型。不过,在这些类型的消息处理函数中可以对消息进行逻辑上的分离。在Java中能够以某种方式处理被分解为多个块的部分消息,而JavaScript尚未支持这种程度的控制能力。如同之前所说,WebSocket是一种非常泛用的协议,它可以在握手时指定所需的逻辑子协议。当不同的系统能够验证所连到的系统支持这种逻辑子协议及扩展时,使用WebSocket进行系统集成就变得容易很多。 WebSocket帧格式允许在它的基础上使用可协商的扩展,这与意味着一般来说帧可能会提供更多的信息,并且可能会引入不同的帧类型。
浏览器端的 JavaScript 实现由于WebSocket协议的握手过程是由客户端发起的,因此需要通过包含了WebSocket接口的JavaScript代码对所有WebSocket操作进行封装。
该接口已经实现了标准化1,并通过接口定义语言(IDL)进行定义,如以下代码所示:
[Constructor(in DOMString url, in optional DOMString protocols)] [Constructor(in DOMString url, in optional DOMString[] protocols)] interface WebSocket { readonly attribute DOMString url; // ready state const unsigned short CONNECTING = 0; const unsigned short OPEN = 1; const unsigned short CLOSING = 2; const unsigned short CLOSED = 3; readonly attribute unsigned short readyState; readonly attribute unsigned long bufferedAmount; // networking attribute Function onopen; attribute Function onmessage; attribute Function onerror; attribute Function onclose; readonly attribute DOMString protocol; void send(in DOMString data); void close(); }; WebSocket implements EventTarget;
WebSocket的构建函数包含两个参数: