吴泽勋
HTML5实现Web服务器推送技术
发表于《唐山师范学院学报》 (ISSN 1009-9115 CN 13-1301/G)2014年5期
Web Server Push Based on HTML5
摘要:如今Web应用越来越丰富,要求Web服务器端能及时主动发送新的信息给客户端。HTML4及以前的版本因为先天协议的缺陷无法实现,而HTML5中WebSocket具有双向通讯功能,能实现Web服务器对客户端的信息推送。要实现WebSocket需要服务器端和客户端双方的支持,客户端主要是浏览器要能实现The WebSocket API草案,而服务器端要能够解析WebSocket协议。因为支持WebSocket协议的Web服务器比较少,所以讨论使用C#语言来建构一个专用的WebSocket服务器。

关键字:推送技术;HTML5;WebSocket
Abstract: Now the Web applications is increasingly growing,which requests to the Web server can promptly take the initiative to send the latest message to the client-side. HTML4 or previous versions can’t be achieved because of congenital defects agreement, while HTML5 WebSocket has two-way communication, can realize the Web server to the client information push. In order to realize the WebSocket server and client sides support, the client browser to The WebSocket is to achieve the API draft, but the server must be able to parse the WebSocket protocol. Because the Web server which support WebSocket protocol is rare, so discussing to the construction of a dedicated WebSocket server by C#.
Key Words:Server Push;HTML5;WebSocket
  随着英特网的高速发展,随着客户端的复杂化,随着接入设备的多样化,越来越多的网络应用采用了浏览器/服务器模式,在该模式下不论客户端什么平台只要安装有浏览器就可以实现,这样大大降低了客户端的调试维护成本。当然事物都有两面性,由于浏览器/服务器模式采用HTTP协议通讯,而HTTP协议作为一个单向连接协议,是个请求/应答协议,即只有浏览器连接到服务器,向服务器发出请求,服务器发回信息。反之服务器不能主动向浏览器发送信息,这样对于很多有实时要求的应用是非常不利的,因为服务器肯定是集中了大部分的信息资源,最早了解信息的变化,所以很多时候需要服务器主动向浏览器发送最新的信息,这种就是服务器推送技术。在HTML5出现以前,浏览器/服务器模式一般是浏览器端采用AJAX轮询或借助第三方插件等技术来模拟服务器推送的过程,而HTML5规范新引入WebSocket的功能,用于解决前台浏览器与后台服务器双向通讯的问题,使用WebSocket技术,后台服务器可以随时向前台浏览器推送消息。下面先讨论一下WebSocket涉及到的相关技术-万维网联盟(W3C)制定的标准规范-浏览器端的WebSocket API和服务器端WebSocket协议。
一、客户端协议——The WebSocket API草案
  要使用WebSocket,浏览器端必须支持HTML5,而浏览器要如何使用WebSocket?万维网联盟提供的The WebSockets API草案第四章中明确提出了WebSocket的接口,并对其做了解释,使用时可以在浏览器中直接用JavaScript来完成,而不需要借助于任何第三方插件。The WebSockets API草案中的接口定义为:
     [Constructor(DOMString url, optional (DOMString or DOMString[]) protocols)]
     interface WebSocket : EventTarget {
     readonly attribute DOMString url;
     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;
     [TreatNonCallableAsNull] attribute Function? onopen;
     [TreatNonCallableAsNull] attribute Function? onerror;
     [TreatNonCallableAsNull] attribute Function? onclose;
     readonly attribute DOMString extensions;
     readonly attribute DOMString protocol;
     void close([Clamp] optional unsigned short code, optional DOMString reason);
     [TreatNonCallableAsNull] attribute Function? onmessage;
      attribute DOMString binaryType;
     void send(DOMString data);
     void send(ArrayBufferView data);
     void send(Blob data);
};
  由上面的接口函数可以看出该初始化函数有一个或两个参数。第一个参数为url,表示要连接的统一资源定位的地址。第二个参数为可选参数,为字符串或字符串数组,表示采用的子协议或供服务器选择的子协议组。在浏览器引用WebSocket时一般分下面几步运行:首先解析URL,获得主机,端口,资源名称。这里要注意的就是出于安全考虑如果端口号为非80端口访问有可能被拒绝。接着分析协议,注意传入的参数字符串中不能含有控制字符或空格。如果初始化参数无误,将建立一个新的WebSocket对象。如果建立失败,则执行关闭WebSocket的算法。
  建立连接的过程使用readyState属性来记录连接状态,它可能出现下列值:
  CONNECTING(0):正在连接,也就是连接没建立。
  OPEN(1):连接建立,可以开始通信了。
  CLOSING(2):正在关闭。
  CLOSED(3):连接关闭。
  连接建立以后就可以使用Send方法用于传输数据,如果连接未建立好或缓冲区满等不能成功发送则返回错误。而Close方法用于关闭连接,如果状态为CLOSING(2)或CLOSED(3)则不执行任何操作。系统在建立连接失败后会自动调用关闭算法。当状态为CLOSED(3),引发关闭事件。如果把状态属性readyState属性设置为CLOSING,也会调用关闭算法。bufferedAmount属性返回发送完成的UTF-8文本的字节数,需要注意的是这里指的是所有发送的字节数,当连接关闭时该值不重置归零。
  以上就是The WebSocket API的基本情况,相对比较简单,特别需要说明的就是这个还是个草案,还没有确定为标准规范,所以修改比较频繁,变化也比较大。在文章一开始已讲过WebSocket为双向协议,不仅需要浏览器的支持也要服务器端的支持,而服务器端采用WebSocket协议,下面讨论一下WebSocket协议。
二、服务器端协议——WebSocket协议
   WebSocket协议不是建立在HTTP之上的,传统的Web服务器一般不支持,服务器端要实现还需要自行构建。根据国际互联网工程任务组(IETF)发布的RFC6455即WebSocket协议可知WebSocket协议订立了HTTP握手的行为来将已经存在的HTTP连接转换为WebSocket连接。WebSocket没有试图在HTTP之上模拟server推送的通道,而是直接在TCP之上定义了帧协议,这样WebSocket能够支持双向的通信。该协议虽然有十四章,但除去官方规范声明、版权声明和相关说明等外基本可以概括为两部分,即浏览器和服务器的握手和数据传输.
   浏览器和服务器要握手,首先由浏览器发送握手请求报头,该报头是无序的。为了兼容基于HTTP协议的服务器端软件及代理, HTTP和WebSocket浏览器连服务器可以同用一个端口.为此, WebSocket浏览器端的握手为HTTP升级请求,具体报头格式如下:
      GET /chat HTTP/1.1
     Host: server.example.com
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
      Origin: http://example.com
     Sec-WebSocket-Protocol: chat, superchat
      Sec-WebSocket-Version: 13
   在该报头中Connection和Upgrade头完成HTTP升级。Sec-WebSocket-Key的作用是保证服务器不能接受非WebSocket连接,特别是防止攻击者使用XMLHttpRequest或表单提交来冒充WebSocket数据来攻击服务器。Origin头用来防止在浏览器中使用WebSocket API来跨域未授权的访问,服务器将被通知WebSocket连接要求来自哪里。如果服务器不接受该源则可以选择中断连接。Sec-WebSocket-Protocol请求头用来指出客户端能接受的子协议。服务器选择可以接受的其中之一并在握手时回应给客户端所选项。
   从服务器返回的握手比客户端握手简单,具体报头如下:
      HTTP/1.1 101 Switching Protocols
     Upgrade: websocket
      Connection: Upgrade
     Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
   第一行为HTTP的状态行,状态代码为101,其他任何代码都被认为握手不成功。Sec-WebSocket-Accept头指出服务器是否接受连接。为了证明握手是被接受的,服务器必须使用两个信息结合起来组建回复,第一信息片时从客户端握手的Sec-WebSocket-Key头中的值(如:dGhlIHNhbXBsZSBub25jZQ==),针对该值, 服务器生成一个值并和Sec-WebSocket-Key的值连接在一起形成一个新值,而服务器握手时返回一个对新值进行SHA-1哈希加密,并使用BASE64编码的返回值,这个返回值就是Sec-WebSocket-Accept的值。
   一旦握手成功则开始传输数据。这是双通道的,对于对方可以独立同时发送数据。在WebSocket协议中, 数据是通过帧序列传输。 客户端发送给服务器的帧有可能被代理之类的拒接或屏蔽,从服务器到客户端的不会被屏蔽。在WebSocket中基本帧协议使用操作码,数据长度,指明的本地扩展和应用数据来定义帧类型,某些位和操作码将保留作为未来协议扩展使用。如果不是在握手时协商好,所有保留的位必须为0,保留的操作码必须没有使用。
   帧格式如下:

  当操作码的值为1,2,3的时候传输的帧为控制帧,用来描述WebSocket的通讯状态。所有的控制帧必须等于或少于125字节并不分段。例如:操作码为1是0x01的时候表示关闭信息,在发送关闭信息以后不能在发送任何数据信息。操作码为2是表示Ping信息,接收到Ping信息的终端必须发送Pong信息回复。Ping和Pong的信息内容必须一致。操作码为3表示Pong信息。 WebSocket协议的大致内容就为上面所述,下面讨论具体是如何实现的。
三、具体实现
1、客户端实现
   HTML5现在还是个发展中的草案,各种客户端对HTML5的支持也是参差不齐,其中支持WebSocket的桌面浏览器有Chrome 6.0及以上版本,Firefox 8及以上版本,IE 10,Maxthon 3及以上版本,Opera 12.10,Safari 5.0及以上版本等;平板方面支持WebSocket的有BlackBerry 10,Chrome,Firefox Mobile 10及以上版本,iOS 4.2及以上版本等,Opera Mobile 12.10,webOS 3.0;移动手机方面有Windows Phone 8 ,Opera Mobile 12.10,iOS 6.0,Firefox Mobile 10,BlackBerry OS 7等。国内最新版浏览器基本采用Chrome共用的内核WebKit或者是双核(WebKit和IE),所以基本上都支持WebSocket。可惜的是在国内平板和移动手机界广为流行的安卓系统不支持HTML5,必须安装其他浏览器。
  根据The WebSocket API草案,只要在支持WebSocket的客户端使用JavaScipt代码new WebSocket("ws://服务器地址")创建WebSocket连接,连接打开成功后监听onmessage事件,可以收到服务器端发送的信息,根据接收到的信息处理本地事务或更新界面等,如果客户端有需要反馈的也可以用send方法发送信息给服务器。
2、服务器端实现
(1)协议模型
   WebSocket协议为HTTP协议的升级,这种升级指的是客户端和服务器端握手时的协议,但是在握手成功后的数据传输就脱离了HTTP协议,所以实现WebSocket协议不能依托于HTTP,但WebSocket还是属于应用层协议,从传输层以上设计即可,传输层采用HTTP协议采用的面向连接TCP协议,传输层以下的协议和WebSocket基本没有关系,所有不在本文的讨论范围。如下图所示:

(2)程序设计
  WebSocket协议为http协议的升级,是新的标准,很多Web服务器软件不支持,因此在这里根据WebSocket协议构建一个WebSocket服务。构建平台采用Windows Server 2003加上.NET框架,Windows Server 2003是现在比较常用的服务器操作系统之一,而.NET框架是微软公司推出的一个全新的编程平台,可以很方便地开发、部署和执行分布式应用程序。程序开发语言采用C#语言实现,C#是微软公司在C++和Java两种语言的基础上特别针对.NET框架开发的一种语言。它结合了VB语言的简单高效和C++语言的强大,是微软公司为了基于互联网的.NET平台特意打造的一种主流语言,也是微软公司主推的一种高级编程语言,也是最受程序开发人员青睐的语言之一。通讯组件采用winSocket,winSocket是微软公司联合其他几家公司共同制定的一套Windows下网络编程接口,是一套开放、支持多种协议的网络编程接口,也是在Internet上进行应用开发最为通用的网络编程接口。平台和工具选好了,接下来讨论一下,自建WebSocket服务器的简单工作流程。
  首先创建TcpListener类对象,监听80端口,等待接收客户端提出的连接要求,如果有客户端提出连接请求,添加一个连接类,并分配给该客户端,由此连接完成与该客户端的通讯。从客户端提交的的握手请求信息中根据握手报头格式判断是否为WebSocket握手信息,如果是则启动握手过程,根据客户端提交的握手信息生成相应的安全密钥,并和其他信息组成握手信息一起发送给客户端。这样客户端与服务器端的握手完成,WebSocket通道建立。握手完成后,服务器端和客户端就可以相互发送信息了,解析信息采用WebScocket协议中的帧格式,服务器端根据第4到7位判断操作码来判断是控制信息还是数据信息,并发送相应信息给客户端。

  由于篇幅所限,详细代码代码不再讨论。
四、小结
  HTML5是Web的未来发展趋势,也是Web开发者的新希望,同时也带来很多原来HTML没有的优点和特性。这些新特性在如今的浏览器新版本中越来越普遍的实现,相信以后绝大部分的浏览器会支持HTML5中的所有技术,当然也会吸引越来越多的开发者学习和使用这些新特性,使Web应用程序发挥着越来越重要的作用。而WebSocket就是其中一项非常重要的新特性,随着时间的推移,以后浏览器的支持会越来越好,用户应用也会越来越多,服务器端和客户端的通讯会越来越方便。
参考文献:
[1] Ian Hickson.The WebSockets API[OL].2014-2-28. http://dev.w3.org/html5/websockets/.
[2] Ian Hickson. The WebSocket Protocol [OL].2011-12. http://datatracker.ietf.org/doc/draft-abarth-thewebsocketprotocol.
[3] 李杰 柳靖 刘淼.HTML5高级程序设计[M].北京:人民邮电出版社,2011.
[4] 刘红伟等.HTML5用户指南[M].北京:机械工业出版社,2011.
[5] Mark Pilgrim.HTML5:Up and Running[M].US:O’Reilly Media Inc,2010.
[6] The Internet Society.RFC2616[OL].2009
[7] Sights.http://html5test.com/compare/feature/communication-webSocket.html.2012