一、从Ajax到WebSocket的演变
1、问题:从服务器获取新数据到浏览器
使用Ajax,游览器可以从服务器抓取新的数据,但浏览器并不知道新数据什么时候可用,而服务器知道。例如:当两个用户在Web应用程序中聊天时,只有服务器知道用户A给用户B发送了一条消息,而浏览器并不知道,只有当浏览器向服务器请求数据时才知道这条消息的存在。这对于Ajax来说,是一个难以解决的问题。
2、解决方案1:频繁轮询
以一个固定的频率,通常是每秒一次,浏览器将发送Ajax请求到服务器查询新数据。如果浏览器有新的数据要发送到服务器,数据将被添加到轮询请求中一起发送到服务器;如果服务器有新的数据要发送到浏览器,它将回复一个含有新数据的响应;如果没有就返回空。
缺点:有大量的请求被浪费了,并且服务器作出了大量无效的响应,再加上创建连接、发送和接收头和关闭连接的时间,大量处理器资源和网络资源被浪费在查找服务器是否有新的数据上面。
3、解决方案2:长轮询
长轮询类似于频繁轮询,不过服务器只有在发送数据时才会响应浏览器,这样更加高效,因为减少了被浪费的计算和网络资源。
缺点:如果浏览器在服务器响应之前有新的数据要发送,浏览器必须创建一个新的并行请求或者终止当前请求创建一个新的请求;TCP和HTTP规范都指定了连接超时,客户端和服务器必须周期性的关闭和重建连接,通常连接需要每60秒关闭一次;HTTP/1.1规范存在着强制的连接限制, 浏览器最多只允许同时创建两个到相同主机名的连接,如果一个连接长期连接到服务器等待数据推送,那么它将减少一半可用于从服务器获取资源的连接。
4、解决方案3:分块编码
分块编码类似于长轮询,它利用了HTTP/1.1的特性,服务器可以在不声明内容长度的情况下响应请求。响应不使用Content-Length响应头时,可以使用Transfer-Encoding:chunked响应头,这将告诉浏览器响应对象将被”分块发送“。
块的内容:一个用于表示块长度的数字、一系列表示块扩展的可选字符和一个CRLF(回车加换行)序列、块包含的数据和另一个CRLF。可以使用任意数目的块,它们可以不是连续的,可以使用任意的时间间隔,当收到块的长度为0时,表示响应结束。
使用块编码解决问题的具体步骤是:在开始的时候创建一个连接,只用于接收服务器发送的事件,来自服务器的每个块都是一个新的事件,它们将触发JavaScript XMLHttpRequest对象的onreadystatechange事件处理器的调用。在需要的时候连接仍然需要刷新,当浏览器需要发送新数据到服务器时,它将使用第2个短生命周期请求。块编码主要解决了长轮询中的超时问题。
缺点:浏览器只能创建两个连接的限制依然存在;旧浏览器在使用长轮询和块编码时,它们会一直在状态栏中显示页面仍在加载的消息。
5、解决方案4:WebSocket
前提:所有的HTTP客户端都可以在请求中包含Connection:Upgrade请求头,为了表示客户端希望升级,在额外的Upgrade头中必须指定一个或多个协议的列表,这些协议必须是兼容HTTP/1.1的协议,如果服务器接受升级请求,那么它将返回状态码101,并在响应的Upgrade头中使用单个值:请求协议列表中服务器支持的第一个协议。
HTTP升级的特性:在升级握手完成之后,它就不再使用HTTP连接,并且我们甚至可以使用一个持久的、全双工TCP套接字连接,这需要指定某些协议,因此就产生了WebSocket协议。
注意:如果服务器上的特定资源只接受HTTP升级请求,当客户端在不请求升级的情况下连接到该资源,服务器将会返回一个426响应,表示必须使用升级。如果客户端使用服务器不支持的协议请求升级,服务器将返回400响应。以上两种情况,服务器在返回时,都可以在Upgrade头中添加服务器所支持的协议列表。
WebSocket连接:它首先将使用非正常的HTTP请求以特定的模式访问一个URL,URL模式ws和wss分别对应HTTP和HTTPS。除了要添加Connection:Upgrade请求头之外,还要添加Connection:websocket请求头,它们将告诉服务器把连接升级为WebSocket协议。在握手完成之后,文本和二进制消息将可以同时在两个方向上进行发送,而不需要关闭和重建连接。
注意:模式ws和模式wss并不是HTTP协议的一部分,特有的WebSocket模式主要用于通知浏览器和API是希望使用SSL/TLS(wss 443端口),还是希望使用不加密的方式(ws 80端口)进行连接。
WebSocket连接成功的最大障碍是HTTP代理,它有可能无法处理HTTP升级请求。使用WebSocket最可靠的方式是一直使用SSL/TLS(wss),代理通常不会干涉SSL/TLS连接,而是让它自己运行。使用了这种策略之后,WebSocket几千可以在所有的环境中正常工作,而且它也是安全的。
二、了解WebSocket API
1、JavaScript客户端API
(1) 创建WebSocket对象
<script>
var connection1 = new WebSocket("ws://localhost:8080/websocket");
var connection2 = new WebSocket("wss://localhost:8080/websocket");
var connection3 = new WebSocket("wss://localhost:8080/websocket","char:v1");
</script>
第一个参数是希望连接的WebSocket服务器要求使用的URL,可选的第二个参数是字符串或者字符串数组,它定义了一个或多个客户端定义的协议,这些协议都是由自己实现的,不受WebSocket技术管理。
(2) 使用WebSocket对象
① websocket对象的属性
//属性readyState:表示当前WebSocket连接的状态,它的值是CONNECTION(0),OPOEN(1),CLOSING(2),CLOSED(3)中的一个
if(connection1.readyState === WebSocket.OPEN){
//do something
}
//属性binaryType:通常应该在实例化WebSocket对