# WebSocket
TIP
WebSockets 是一种先进的技术。它可以在用户的浏览器和服务器之间打开交互式通信会话。使用此 API,您可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。
构建网络应用的过程中,我们经常需要与服务器进行持续的通讯以保持双方信息的同步。在WebSocket
之前,大都是通过传统轮询的方式。
# 传统轮询
传统轮询采用的是借助setInterval
和setTimeout
函数来不间隔的发送ajax
请求。
setInterval(function() {
$.get('/Technical-tree', function(data, status) {
console.log(data)
})
}, 10000)
2
3
4
5
上面的程序会以 10s 间隔的频率请求数据,但我们需要考虑网络情况接口速度等问题。服务器从接受请求到最后将数据返回到客服端的总时间有可能会超过 10s。这种情况下,前一次的数据尚未接受下一次请求就发出去了。所有我们需要下面这种方式:
function poll() {
setTimeout(function() {
$.get('/Technical-tree', function(data, status) {
console.log(data)
// 发起下一次请求
poll()
})
}, 10000)
}
2
3
4
5
6
7
8
9
程序首先设置 10 秒后发起请求,当数据返回后再隔 10 秒发起第二次请求,以此类推。这样的话虽然无法保证两次请求之间的时间间隔为固定值,但是可以保证到达数据的顺序。
# 长轮询
上述两种花方式在每次请求时都会发起一个 https 请求,但新数据并非每时每刻都会产生。当发起的请求次数过多,不可避免地对服务器造成负担。这时我们可以通过长轮询来解决这个问题。
长轮询与以下将要提到的服务器发送事件和 WebSocket 不能仅仅依靠客户端 JavaScript 实现,我们同时需要服务器支持并实现相应的技术。
长轮询的基本思想是在每次客户端发出请求后,服务器检查上次返回的数据与此次请求时的数据之间是否有更新,如果有更新则返回新数据并结束此次连接,否则服务器 hold 住此次连接,直到有新数据时再返回相应。而这种长时间的保持连接可以通过设置一个较大的 HTTP timeout
实现。
长轮询可以有效地解决传统轮询带来的带宽浪费,但是每次连接的保持是以消耗服务器资源为代价的。尤其对于 Apache+PHP 服务器,由于有默认的 worker threads 数目的限制,当长连接较多时,服务器便无法对新请求进行相应。
# 服务器发送事件(Server-Sent Event)
服务器发送事件 (opens new window)(以下简称 SSE)是HTML 5
规范的一个组成部分,可以实现服务器到客户端的单向数据通信。通过 SSE ,客户端可以自动获取数据更新,而不用重复发送 HTTP 请求。一旦连接建立,“事件”便会自动被推送到客户端。服务器端 SSE 通过 事件流(Event Stream) 的格式产生并推送事件。事件流对应的 MIME 类型 为 text/event-stream
,包含四个字段:event、data、id和retry
。event 表示事件类型,data 表示消息内容,id 用于设置客户端 EventSource
对象的 last event ID string
内部属性,retry 指定了重新连接的时间。
从服务器接受事件:
evtSource.addEventListener(
'ping',
function(e) {
var newElement = document.createElement('li')
var obj = JSON.parse(e.data)
newElement.innerHTML = 'ping at ' + obj.time
eventList.appendChild(newElement)
},
false
)
2
3
4
5
6
7
8
9
10
11
# WebSocket
WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。
HTTP 协议无法实现服务器主动向客户端发起消息,WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 Web 浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。
# WebSocket 客户端
// 初始化一个 WebSocket 对象
var ws = new WebSocket('ws://localhost:9998/echo')
// 建立 web socket 连接成功触发事件
ws.onopen = function() {
// 使用 send() 方法发送数据
ws.send('发送数据')
alert('数据发送中...')
}
// 接收服务端数据时触发事件
ws.onmessage = function(evt) {
var received_msg = evt.data
alert('数据已接收...')
}
// 断开 web socket 连接成功触发事件
ws.onclose = function() {
alert('连接已关闭...')
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# WebSocket 构造函数
WebSocket(url[, protocols])
url
WebSocket 服务器将响应的 URL
protocols
| 可选单协议字符串或者包含协议字符串的数组。这些字符串用于指定子协议,这样单个服务器可以实现多个 WebSocket 子协议(例如,您可能希望一台服务器能够根据指定的协议处理不同类型的交互)protocol)。如果不指定协议字符串,则假定为空字符串。
# 方法
- webSocket.onopen:用于指定连接成功后的回调函数
ws.onopen = function() {
ws.send('Hello Server!')
}
2
3
- webSocket.onclose: 用于指定连接关闭后的回调函数
ws.onclose = function(event) {
var code = event.code
var reason = event.reason
var wasClean = event.wasClean
// handle close event
}
ws.addEventListener('close', function(event) {
var code = event.code
var reason = event.reason
var wasClean = event.wasClean
// handle close event
})
2
3
4
5
6
7
8
9
10
11
12
13
# WebSocket 服务端
常用的 node 实现有下面几种: