介绍
RFC 6455 规范中描述的 WebSocket 协议提供了一种通过长连接在浏览器和服务器之间交换数据的方法。 数据可以作为“数据包”双向传递,而不会中断连接并且不需要额外的 HTTP 请求。
WebSocket 特别适用于需要持续数据交换的服务,例如 网络游戏、实时交易系统等。
简单实例
要打开 websocket 连接,我们需要使用 url 中的特殊协议 ws 创建新的 WebSocket:
let socket = new WebSocket("ws://127.0.0.1:8001");
还有加密的 wss:// 协议。 这就像 websocket 的 HTTPS。 一旦创建了socket,我们就应该监听它上面的事件。 总共有4个事件:
- open – 连接建立
- message – 消息发送
- error – 错误处理
- close – 连接关闭
如果我们想发送消息,可以使用: socket.send(data) ,下面是一个简单实例基于js:
let socket = new WebSocket("wss://127.0.0.1:8001/websocket/demo/hello");
socket.onopen = function(e) {
alert("[open] Connection established");
alert("Sending to server");
socket.send("My name is John");
};
socket.onmessage = function(event) {
alert(`[message] Data received from server: ${event.data}`);
};
socket.onclose = function(event) {
if (event.wasClean) {
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
alert('[close] Connection died');
}
};
socket.onerror = function(error) {
alert(`[error]`);
};
websocket创建
new WebSocket(url)后,它会立即开始连接。 在连接期间,浏览器(headers)询问服务器:“是否支持Websocket ?” 如果服务器响应“是”,那么会话将继续在WebSocket 协议中进行,非HTTP。 下面一个简单的请求header信息
GET /chat
Host: javascript.info
Origin: https://127.0.0.1:8001
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13
- Origin – 客户端页面的来源,例如https://127.0.0.1:8001。 WebSocket 对象本质上是跨域的。 没有特殊的标题或其他限制。 旧服务器无论如何都无法处理 WebSocket,因此不存在兼容性问题。 但是 Origin 标头很重要,因为它允许服务器决定是否与该网站通信WebSocket
- Connection: Upgrade——表示客户端想要更改协议。
- Upgrade:websocket——请求的协议是“websocket”。
- Sec-WebSocket-Key – 浏览器生成的随机密钥,用于确保服务器支持 WebSocket 协议。 防止代理缓存任何后续通信是随机的。
- Sec-WebSocket-Version - WebSocket 协议版本
如果服务端支持WebSocket,它会发生相应码101:
101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
这里的 Sec-WebSocket-Accept 是 Sec-WebSocket-Key,使用特殊算法重新编码。 浏览器能够解析该编码, 来确认服务端支持该WebSocket协议。 之后,使用 WebSocket 协议传输数据,我们很快就会看到它的结构(“frames”)。 而不是传统意义的 HTTP
扩展和子协议
请求header中可以增加: Sec-WebSocket-Extensions 和 Sec-WebSocket-Protocol
let socket = new WebSocket("wss://127.0.0.1:8001/chat", ["soap", "wamp"]);
请求如下
GET /chat
Host: javascript.info
Upgrade: websocket
Connection: Upgrade
Origin: https://127.0.0.1:8001
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap, wamp
相应
101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap
- Sec-WebSocket-Extensions:deflate-frame 表示浏览器支持数据压缩。 扩展是与传输数据相关的东西,是扩展 WebSocket 协议的功能。 Sec-WebSocket-Extensions 由浏览器自动发送,其中包含它支持的所有扩展的列表。
- Sec-WebSocket-Protocol:soap, wamp 意味着我们不仅要传输任何数据,还要传输 SOAP 或 WAMP(“The WebSocket Application Messaging Protocol”)协议中的数据。 WebSocket 子协议在 IANA 目录中注册。 因此,此headers描述了我们将要使用的数据格式。
数据传输
WebSocket 通信由“frames”——数据片段组成,可以从任何一方发送,并且可以有以下几种:
- text frames ——包含各方相互发送的文本数据。
- binary data frames ——包含各方相互发送的二进制数据。
- ping/pong frames —— 用于检查连接,从服务器发送,浏览器自动响应这些。
- connection close frame —— 和其他一些服务框架。
在浏览器中,我们只直接使用文本或二进制帧 WebSocket .send() 方法可以发送文本或二进制数据 调用 socket.send(body) 允许使用字符串或二进制格式的正文,包括 Blob、ArrayBuffer 等。无需设置:以任何格式发送即可。
当我们收到数据时,文本总是以字符串的形式出现。对于二进制数据,我们可以在 Blob 和 ArrayBuffer 格式之间进行选择。
这是由 socket.binaryType 属性设置的,默认为“blob”,因此二进制数据以 Blob 对象的形式出现。
socket.binaryType = "arraybuffer";
socket.onmessage = (event) => {
};
发送限制
想象一下,我们的应用程序正在生成大量要发送的数据。 但是用户的网络连接速度很慢。
我们可以一次又一次地调用 socket.send(data)。 但是数据将被缓冲(stored)在内存中,并仅在网络速度允许的情况下以最快的速度发送出去。
socket.bufferedAmount 属性存储了此时有多少字节保持缓冲,等待通过网络发送。
setInterval(() => {
if (socket.bufferedAmount == 0) {
socket.send(moreData());
}
}, 100);
连接关闭
通常,当一方想要关闭连接时(浏览器和服务器具有相同的权限),他们会发送一个带有数字代码和文本原因的“connection close frame”。
socket.close([code], [reason]);
- code 一个特殊的 WebSocket code(可选)
- reason 关闭原因(可选)
socket.close(1000, "Work complete");
socket.onclose = event => {
};
最常见的代码值:
- 1000 - 默认的正常关闭(如果没有提供代码则使用)
- 1006 - 无法手动设置此类代码,表示连接丢失(无关闭帧)
- 1001 - 服务器正在关闭,或浏览器离开页面
- 1009 – 消息太大而无法处理
- 1011 - 服务器上出现意外错误
- …
连接状态
要获取连接状态,另外还有带有值的 socket.readyState 属性:
- 0 – “CONNECTING”:连接尚未建立,
- 1 – “OPEN”:打开并连接,
- 2 – “CLOSING”:连接正在关闭,
- 3 – “CLOSED”:连接已关闭。
|