背景
过去,使用 ajax 轮询或 long poll 方式处理实时性要求比较高的应用。
缺点:1、http 请求是无状态的,每次请求都会带 header;2、server 需要为每个 client 维护两个 TCP 连接(一个用于给 client 发送信息,一个用户接收数据)
HTML5 中出现了一个新的协议 websocket,支持在客户端和服务器间使用全双工通信,可用于实时性要求比较高的应用,如游戏,股票交易,多用户协作等。
协议概览
协议分为两个部分:握手和数据传输
握手
websocket 是基于 TCP 协议,通过 HTTP 协议实现 Upgrade 请求,使用 80/443 端口
1、 客户端发送一个请求:
GET /xxx HTTP/1.1
Request URL: wss://xxx.xxx.xxx/ws
Connection: Upgrade
Host: xxx.xxx.xxx
Origin: https://xxx.xxx
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: Wul5+39fW+VU4hdALTiCWw==
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: chat, superchat
Upgrade: websocket
2、 然后服务器会返回下列东西,表示已经接受到请求,成功建立 Websocket,告诉客户端即将升级的是 Websocket 协议
HTTP/1.1 101 Switching Protocols
Connection: upgrade
Date: Thu, 09 Aug 2018 06:19:08 GMT
Sec-WebSocket-Accept: ERPVeRRPyTlkPDZoozDLvc0mBsM=
Sec-WebSocket-Protocol: chat
Server: nginx/1.13.12
Upgrade: websocket
-
Sec-WebSocket-Key 是一个 Base64 encode 的值,这个是浏览器随机生成的
-
Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同 URL 下,不同的服务所需要的协议
-
Sec-WebSocket-Version 是告诉服务器所使用的 Websocket Draft(协议版本)
-
Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key
-
Sec-WebSocket-Protocol 则是表示最终使用的协议
Sec-WebSocket-Accept 计算方式: base64-encoded(SHA-1(Sec-WebSocket-Key+258EAFA5-E914-47DA-95CA-C5AB0DC85B11))
传输数据
支持格式:textual data(文本格式),binary data(二进制数据),control frames(控制帧)等
WebSocket URIs
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
数据帧
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
客户端示例
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
socket.binaryType = "arraybuffer";
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
常量&属性
WebSocket.readyState
对照以下常量常量判断 WebSocket 连接 当前所处的状态:
WebSocket.CONNECTING:值为0,表示正在连接。
WebSocket.OPEN:值为1,表示连接成功,可以通信了。
WebSocket.CLOSING:值为2,表示连接正在关闭。
WebSocket.CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
WebSocket.binaryType
使用二进制的数据类型连接
WebSocket.bufferedAmount
返回已经被send()方法放入队列中但还没有被发送到网络中的数据的字节数。
WebSocket.onclose
用于指定连接关闭后的回调函数
WebSocket.onerror
用于指定连接失败后的回调函数
WebSocket.onmessage
用于指定当从服务器接受到信息时的回调函数
WebSocket.onopen
用于指定连接成功后的回调函数
方法
WebSocket.close([code[, reason]])
关闭当前链接
WebSocket.send(data)
向服务器发送数据
浏览器兼容
很显然,要支持WebSocket通信,浏览器得支持这个协议,目前支持的浏览器:
Chrome
Firefox
IE >= 10
Sarafi >= 6
Android >= 4.4
iOS >= 8
参考
developer.mozilla.org/zh-CN/docs/…
欢迎关注“web笔记本”,领取前端学习资料,加入高质量交流群一起学习~