概念
websocket
是一个网络传输协议,可以在单个tcp连接上进行全双工通信,位于OSI
7层网络模型(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)中的应用层。
为什么需要websocket
传统的http协议只能由客户端发起请求,然后服务端这边才能响应请求,也就是把数据发给客户端,服务端这边没办法主动发送数据给客服端。
基于单向请求的特点,如果客户端这边想不断获取服务端状态的变化,那么就要借助轮询。常见的轮询分为两种:短轮询和长轮询(Comet)。
我们可以用两段代码来直观的体现短轮询和长轮询的特点
短轮询
function polling(url) {
let xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.onload = function() {...}
xhr.send()
}
// 每隔30s发送一次请求
setInterval(() => {
polling('./api/getData')
}, 30000)
长轮询
function longPolling(url) {
let xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.onload = function() {
// 请求结束后再接着发送请求
longPolling('./api/getData')
}
xhr.send()
}
longPolling('./api/getData')
从上面我们其实可以看出短轮询和长轮询的特点。短轮询每隔固定的时间向服务器发送请求。这种方式实时性比较差,而且很浪费带宽资源(有效请求很少)。而长轮询服务器收到请求后会将请求挂起,等请求的数据发生了变化后,再返回新的数据。长轮询解决了实时性的问题,但如果服务器这边收到多个长轮询请求会导致服务器的多个线程被挂起,进而导致服务器大量资源的浪费。
在这种情况下,HTML5
定义了新的websocket
协议,使得服务端可以主动向客户端发送消息,这样既可以节约带宽和服务端资源,也能够更加实时地进行通信。
机制
websocket
其实也利用了http
协议。websocket
通过握手机制建立客户端和服务端之间的连接,而握手机制正是通过http
协议完成的。
websocket
连接必须由浏览器发起,浏览器会发送一个http
请求。
GET ws://example.com:80/some/path HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Origin: http://localhost:3000
Sec-WebSocket-Key: client-random-string
Sec-WebSocket-Version: 13
这个请求和普通的http
请求有些不同点:
- 请求的URL是以
ws://
开头的 - 请求头
Upgrade: websocket
和Connection: Upgrade
表示这个连接将要被转换为websocket连接 - 两个
websocket
相关的请求头,Sec-WebSocket-Key
用来标识连接,Sec-WebSocket-Version
指定websocket版本
服务器收到请求后,会返回http
响应。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string
这个响应没什么好说的,唯一需要注意的就是状态码101
表示协议即将被切换,即由http
协议切换成Upgrade: websocket
指定的websocket
协议。
握手结束后,客户端和服务端之间的连接就建立起来了,后续便可以进行双向数据传输了。传输的数据支持两种格式,文本和二进制数据。
API
构造函数
构造函数用于新建websocket
实例
let ws = new WebSocket('ws://localhost:8080');
实例属性
-
binaryType
:二进制的类型(blob
或者ArrayBuffer
) -
bufferedAmount
:未发送出去的字节数量,可以用来判断数据是否发送完毕 readyState
-
CONNECTING
:值为0,表示正在连接。 -
OPEN
:值为1,表示连接成功,可以通信了。 -
CLOSING
:值为2,表示连接正在关闭。 -
CLOSED
:值为3,表示连接已经关闭,或者打开连接失败。
- 四个回调
-
onclose
:连接关闭后执行的回调 -
onerror
:报错后执行的回调 -
onopen
:连接成功后执行的回调 -
onmessage
:收到数据后执行的回调
方法
-
close
:用来关闭websocket连接
ws.close();
-
send
:发送数据
if (ws.readyState !== WebSocket.OPEN) {
console.log("连接未建立,还不能发送消息");
return;
}
// 发送的数据可以是文本 可以是blob 可以是ArrayBuffer
ws.send('message')
const buffer = new ArrayBuffer(128);
ws.send('buffer')
const blob = new Blob([buffer]);
ws.send(blob)
事件
对应上面那四个回调
open
close
message
error
事件的回调可以通过实例的属性指定,也可以借助addEventListener
绑定,addEventListener
可以绑定多个回调。