为什么要引入WebSocket,我们先了解现有技术客户端如何及时获得服务器更新,他们有什么缺点,再来看为什么我们需要WebSocket。
如何及时获得服务器更新
服务器端的资源经常在更新,客户端需要尽量及时地知道这些更新从而展示给用户。
HTTP 1.1的做法如下图所示,通过 Ajax 轮询来获取服务器端资源的变化。
轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
现有的用于服务器和客户端之间的双工通信技术
这些技术如下 –
- 轮询(Polling)
- 长轮询(Long Polling)
- 数据流(Streaming)
- AJAX
轮询(Polling)
轮询可以定义为一种方法,无论传输中存在哪些数据,它都执行周期性请求。定期请求以同步方式发送。客户端在指定的时间间隔内向服务器发出定期请求。服务器的响应包括可用数据或其中的一些警告消息。
这种方式下,是不适合获取实时信息的,客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。客户端会轮询,有没有新消息。这种方式连接数会很多,一个接受,一个发送。而且每次发送请求都会有 HTTP 的 Header,会很耗流量,也会消耗 CPU 的利用率。
这个阶段可以看到,一个 Request 对应一个 Response,一来一回一来一回。
长轮询(Long Polling)
顾名思义,长轮询包括类似轮询的技术。客户端和服务器保持连接处于活动状态,直到获取某些数据或发生超时。如果由于某些原因导致连接丢失,则客户端可以重新开始并执行顺序请求。
长轮询是对轮询的改进版,客户端发送 HTTP 给服务器之后,有没有新消息,如果没有新消息,就一直等待。直到有消息或者超时了,才会返回给客户端。消息返回后,客户端再次建立连接,如此反复。这种做法在某种程度上减小了网络带宽和 CPU 利用率等问题。
这种方式也有一定的弊端,实时性不高。如果是高实时的系统,肯定不会采用这种办法。因为一个 GET 请求来回需要 2个 RTT,很可能在这段时间内,数据变化很大,客户端拿到的数据已经延后很多了。
另外,网络带宽低利用率的问题也没有从根源上解决。每个 Request 都会带相同的 Header。
数据流(Streaming)
数据流是实时数据传输的最佳选择。服务器保持连接打开并与客户端一起激活,直到并且除非提取所需的数据。在这种情况下,连接是无限期打开的。流传输包括HTTP标头,这会增加文件大小,增加延迟。这是一个主要缺点。
AJAX
AJAX基于Javascript的 XmlHttpRequest
对象。它是Asynchronous Javascript和XML的缩写形式。XmlHttpRequest
对象允许执行Javascript而无需重新加载完整的网页。AJAX仅发送和接收网页的一部分。
使用 XmlHttpRequest
对象调用AJAX的代码片段如下 –
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
与WebSocket相比,AJAX的主要缺点是 –
- 它们需要发送HTTP标头,这使数据量更大。
- 通信是半双工的。
- Web服务器消耗更多资源。
为什么我们需要WebSocket
从上面这几种演进的方式来看,也是不断改进的过程。
短轮询效率低,非常浪费资源(网络带宽和计算资源)。有一定延迟、服务器压力较大,并且大部分是无效请求。
长轮询虽然省去了大量无效请求,减少了服务器压力和一定的网络带宽的占用,但是还是需要保持大量的连接。
最后到了基于流的方式,在服务器往客户端推送,这个方向的流实时性比较好。但是依旧是单向的,客户端请求服务器依然还需要一次 HTTP 请求。
那么人们就在考虑了,有没有这样一个完美的方案,即能双向通信,又可以节约请求的 header 网络开销,并且有更强的扩展性,最好还可以支持二进制帧,压缩等特性呢?
于是人们就发明了这样一个目前看似“完美”的解决方案 —— WebSocket。
在 HTML5 中公布了 WebSocket 标准以后,直接取代了 Comet 成为服务器推送的新方法。
Comet 是一种用于 web 的推送技术,能使服务器实时地将更新的信息传送到客户端,而无须客户端发出请求,目前有两种实现方式,长轮询和 iframe 流。