Websocket协议

之前一般实现推送功能都是采用轮询的方法,轮询是指在特定的时间间隔,向服务端发起一个HTTP请求,然后由服务端返回数据给浏览器。HTTP协议是惰性的,只有客户端发送请求,服务端才会返回数据。

而Websockt属于服务端推送技术,和HTTP协议同属应用层,可以实现持久连接的全双工通信。

Ajax短轮询

Ajax短轮询就是客户端周期性地想服务端发起请求,不管服务端是否需要发送数据,都会响应请求,即使发送的数据为空。即每个Request都会有一个Response。HTTP/1.1实现了持久连接(建立一个TCP连接,可以发起多个请求)和管线化技术(可以异步发送请求),使得HTTP在建立起一个TCP连接可以发送多个异步请求。

Ajax短轮询

缺点:需要浏览器不断地想服务器发送请求,每次请求都带上很长的头部请求字段,严重浪费带宽。

服务端推送(Comet)

Ajax长轮询

长轮询是短轮询的一种变体。在客户端发送请求后,服务端并不立即响应,只有等到获得最新数据后,才会返回数据。如果长时间数据没有更新,一段时间后,请求就会超时。客户端收到超时信息后,需要重新向客户端发送一个HTTP请求。

Ajax长轮询

长轮询和短轮询的差别在于服务端是否立即响应,长轮询可以减少大量无效请求。

HTTP流

服务器推送技术(HTTP流),在客户端只发起一次HTTP请求,服务器保持连接状态,在数据更新之后,服务器会传输数据,否则保持连接状态。此时一个requset对应多个response。

H5 服务端推送

H5实现了两个服务端推送接口:SSEWebSocket

SSE

SSE(Server-Send Events,服务端推送事件)用于创建到服务端的单向连接,服务端可以通过这个连接发送任意数量的数据。实现SSE有几点要求:

  1. 服务器响应的MIME类型必须是text/event-stream
  2. 必须按照指定的格式输出。
1
2
3
4
5
6
7
8
// EventSource 接收的参数必须同源
// 使用message事件监听从服务器收到的消息,并存储在event.data对象里。
let source = new EventSource('index.php')

source.onmessage = function(e){
var data = e.data
console.log(data)
}

WebSocket

WebSocket与HTTP同属应用层协议,都是TCP/IP的子集。HTTP协议是单向通信协议,只有客户端发起HTTP请求,服务端才会返回数据。而WebSocket协议是双向通信协议,在建立连接之后,客户端和服务器都可以主动向对方发送或接受数据。WebSocket协议建立的前提是借助HTTP协议,建立之后,持久连接的双向通信就与HTTP协议无关了。

在JS创建WebSocket后,会有一个HTTP请求发送到服务端,服务端响应后,建立的连接会用HTTP升级将HTTP协议转换为WebSocket协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// url没有同源的限制
var ws = new WebSocket(url)

// 连接open事件 连接成功后向服务端发送 纯文本信息(对象需要序列化)
ws.onopen = function(){
if(ws.readyState === WebSocket.OPEN){
ws.send('Hello World')
}
}

// 监听message事件,数据存储在e.data中
ws.onmessage = function(e){
var data = e.data
console.log(data)
}

// 监听error事件,发生错误时触发,连接不能持续
ws.onerror = function(){
console.log('websocket connecting error!!')
}

// 监听close事件
ws.onclose = function(){
let clean = e.wasClean // 是否已经关闭
let code = e.code // 服务器返回的数值状态码。
let reason = e.reason //服务器返回的消息。
}

WebSocket 不支持 DOM2 语法(ws.addEventListener('onopen',() => {}))为事件绑定事件处理,因此使用 DOM0 级语法(ws.onopen = () => {})

WebSocket是应用层协议,是TCP/IP协议的子集,通过HTTP/1.1协议的101状态码进行握手。

请求字段