HTML5中的服务器‘推送’技术 -WebSocket

来源:互联网 发布:数据恢复精灵 4.2.1 编辑:程序博客网 时间:2024/05/16 13:59

 转帖:http://www.developersky.net/thread-81-1-1.html

 除了Server-Sent Event之外,即将到来的HTML5标准还包含了WebSockets。WebSocket使得我们可以建立双向的通信通道。和Server-Sent Event相反,WebSocket协议不是建立在HTTP之上的。但是WebSocket协议订立了HTTP握手的行为来将已经存在的HTTP连接转换为WebSocket连接。WebSocket没有试图在HTTP之上模拟server推送的通道,而是直接在TCP之上定义了帧协议,因此WebSocket能够支持双向的通信。
和server-Sent Event规范相同,WebSocket定义了API和相应的协议。WebSocket API规范中包含一个新的HTML元素:WebSocket。下面的代码就是一个使用WebSocket的HTML的例子:

当创建一个WebSocket实例的时候,相应的WebSocket连接就会被建立。构造函数需要两个参数(第二个参数是可选的)。第一个参数是WebSocketURL,该参数定义了要连接的URL。WebSocketURL以ws或wss开头。ws开头的是普通的WebSocket连接,wss开头的是安全的WebSocket连接(类似https)。第二个参数是要使用的子协议,该参数是可选的。
我们可以定义WebSocket实例的onMessage处理函数,每次收到消息的时候,该处理函数都会被调用。如果要发送消息到服务器端,可以通过WebSocket的send()方法发送消息。
当一个新的WebSocket实例被创建的时候,首先底层的user agent会建立一个普通的到指定URL的HTTP(S)连接,然后对该连接进行升级(upgrade)。HTTP规范在消息头中定义了upgrade域来进行该操作。Upgrade头提供了简单的机制来将HTTP协议转换为其他的不兼容的协议。WebSocket利用了HTTP协议的这个能力来讲新创建的HTTP连接转换为WebSocket连接。同时添加了WebSocket-Protocal来制定要使用的子协议。
下面是一个Upgrade的Request和Response消息的例子。

当收到HTTP response消息之后,只有WebSocket帧能够通过该连接传送,所有的数据都会根据WebSocket协议进行转换。WebSocket帧能够在任何时候向任何方向发送。WebSocket协议定义了两种类型的帧:文本帧(text frame)和二进制帧(binary frame)。文本帧以字节0x00开头,以字节0xFF结尾。当中的文本内容要转换成UTF8编码。所以为了打包,文本帧需要添加两个额外的字节。下面是两个文本帧的例子:

如果要传送二进制数据,就需要使用二进制帧。二进制帧以字节0x80开始。和文本帧相反,二进制帧没有结束标志。在二进制帧的开始标识字节(0x80)之后就是长度字节。长度字节的字节数是不固定的,根据需要来决定。下面是两个例子,第一个例子中需要传递的数据量很小,因此长度字节就只有一个字节;第二个例子中需要传递的数据量比较大,就使用了2个字节作为长度字节。

因为JavaScript不能操作字节数组形式的二进制数据,因此二进制帧目前无法被JavaScript使用。除了文本帧和二进制帧之外,WebSocket协议将来还有可能引入新的类型的帧格式。WebSocket帧在设计的时候就考虑了支持新的帧类型。
WebSocket的连接可以在任何时候关闭,不需要额外的‘结束连接’字节或帧。
管理WebSocket的额外开销是很小的。如Bayeux和BOSH这样的Comet协议是建立在HTTP协议之上的,这就迫使这些协议要实现复杂的会话和连接的管理。而WebSocket是建立在TCP协议之上的,不会碰到这些由于HTTP协议的局限性引起的麻烦。
另一方面,WebSocket基本上没有实现可靠性的功能。它既没有包括重建连接的处理,也不支持向Server-Sent Event那样的保证消息成功传递的机制。而且,由于WebSocket不是基于HTTP协议的,因此也无法利用HTTP协议内建的可靠性的特性。例如HTTP协议支持当网络故障是的自动重试功能(一个Get方法应该不会改变任何服务器端的资源的状态,因此我们可以重复的执行Get方法而没有任何的副作用。当网络故障的时候,浏览器或HttpClients可以自动重新执行Get方法)。
由于WebSocket没有这些功能,因此在应用程序(或者说子协议)层面上我们就需要实现可靠性的功能,包括发送“维持通信”消息来避免代理服务器在一段时间没有消息之后关闭连接。另外,在多个页面之间共享WebSocket通常会带来麻烦。和Server-Sent Event不同,WebSocket会包含一个难以共享的上游的管道。例如,并发的读写操作必须要同步,这并不是一个简单的任务。因此当使用WebSocket的时候,‘每个服务器一个连接’必须要仔细考虑。
Web浏览器限制浏览器端的编程语言(如JavaScript)连接到其他的服务器,因此web页面上的WebSocket只能连接和当前页面在同一个域中的服务器。对于独立的WebSocket客户端则没有这个限制。如下面的例子中我们通过Java客户端来使用WebSocket:

下面是一个WebSocket服务器实现的简单例子。服务器要实现两个接口:IHttpRequestHandler和IWebSocketHandler。IHttpRequestHandler接口用于处理普通的HTTP请求,IWebSocketHandler据诶和用于处理WebSocket连接和消息。当一个标准的HTTP请求到来的时候(不包含upgrade请求),IHttpRequestHandler的onRequest()方法会被调用。如果客户端打开WebSocket,服务器会收到HTTP upgrade请求,IWebSocketHandler的onConnect()方法会被调用。每次收到WebSocket的消息,IWebSocketHandler的onMessage()方法都会被调用。
在onConnect()方法中,我们可以检查一些先决条件。例如检查要求的子协议是否被支持,下面的例子中如果要求的子协议不被支持,则会返回一个错误状态。下面的例子啊红我们还驾车了请求头中的origin字段。Origin字段是HTTP Origin Header RFC(还是草案)定义的,该字段由浏览器自动设置。

上面的例子中,我们会使用内部的白名单来检查origin header,并拒绝我们不期望的请求。这样,我们可以防止某些攻击者将公共页面上的JavaScript代码拷贝并添加到他们自己的页面中。这种情况下,浏览器会将origin header设置为攻击者自己的页面所在的域,服务器处理升级请求的时候就会拒绝该请求。这个技术用来防范Cross-Site Request攻击。Origin header规范和WebSocket协议规范是相互独立的,但是WebSocket协议定义了WebSocket-Origin header,该字段必须被包含在WebSocket升级请求中。

因为WebSocket连接是有HTTP连接升级而来,因此WebSocket协议也可以和HTTP代理服务器一起工作。浏览器总是和代理服务器通信,代理服务器将HTTP request和response进行转发。当浏览器使用HTTP代理并打开一个WebSocket的时候,首先浏览器会打开一个到代理服务器的通道。通过发送HTTP/1.1连接请求,浏览器要求HTTP代理创建一个到被代理的服务器(WebSocket服务器)的TCP连接。当连接建立以后,HTTP代理服务器的功能就被缩小为到WebSocket服务器的TCP代理。使用这个被代理的连接,浏览器发送WebSocket升级请求到WebSocket服务器。下面的列表描述了整个过程。

  

 

 

 

 

转帖:http://www.developersky.net/thread-81-1-1.html

原创粉丝点击