RFC 6455 规范中描述的 WebSocket 协议提供了一种通过长连接在浏览器和服务器之间交换数据的方法。 数据可以作为“数据包”双向传递,而不会中断连接并且不需要额外的 HTTP 请求。
WebSocket 特别适用于需要持续数据交换的服务,例如 网络游戏、实时交易系统等。
要打开 websocket 连接,我们需要使用 url 中的特殊协议 ws 创建新的 WebSocket:
let socket = new WebSocket("ws://127.0.0.1:8001");
还有加密的 wss:// 协议。 这就像 websocket 的 HTTPS。
一旦创建了socket,我们就应该监听它上面的事件。 总共有4个事件:
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 {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
alert('[close] Connection died');
}
};
socket.onerror = function(error) {
alert(`[error]`);
};
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
如果服务端支持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
WebSocket 通信由“frames”——数据片段组成,可以从任何一方发送,并且可以有以下几种:
在浏览器中,我们只直接使用文本或二进制帧
WebSocket .send() 方法可以发送文本或二进制数据
调用 socket.send(body) 允许使用字符串或二进制格式的正文,包括 Blob、ArrayBuffer 等。无需设置:以任何格式发送即可。
当我们收到数据时,文本总是以字符串的形式出现。对于二进制数据,我们可以在 Blob 和 ArrayBuffer 格式之间进行选择。
这是由 socket.binaryType 属性设置的,默认为“blob”,因此二进制数据以 Blob 对象的形式出现。
socket.binaryType = "arraybuffer";
socket.onmessage = (event) => {
// event.data is either a string (if text) or arraybuffer (if binary)
};
想象一下,我们的应用程序正在生成大量要发送的数据。 但是用户的网络连接速度很慢。
我们可以一次又一次地调用 socket.send(data)。 但是数据将被缓冲(stored)在内存中,并仅在网络速度允许的情况下以最快的速度发送出去。
socket.bufferedAmount 属性存储了此时有多少字节保持缓冲,等待通过网络发送。
setInterval(() => {
if (socket.bufferedAmount == 0) {
socket.send(moreData());
}
}, 100);
通常,当一方想要关闭连接时(浏览器和服务器具有相同的权限),他们会发送一个带有数字代码和文本原因的“connection close frame”。
socket.close([code], [reason]);
socket.close(1000, "Work complete");
socket.onclose = event => {
// event.code === 1000
// event.reason === "Work complete"
// event.wasClean === true (clean close)
};
最常见的代码值:
要获取连接状态,另外还有带有值的 socket.readyState 属性: