WebSocket基本介绍
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
WebSocket 状态
在上面代码中我们通过WebSocket()
构造函数来构造一个ws
实例。对应的这个实例中有只读属性 readyState 表示连接状态四个状态,对应的分别有四个不同的值,具体如下:
状态 | 值 | 说明 |
---|---|---|
WebSocket.CONNECTING | 0 | 表示连接尚未建立 |
WebSocket.OPEN | 1 | 表示连接已建立,可以进行通信 |
WebSocket.CLOSING | 2 | 表示连接正在进行关闭 |
WebSocket.CLOSED | 3 | 表示连接已经关闭或者连接不能打开 |
WebSocket 事件
整个ws
建立的过程有四个比较重要的事件,分别是:
- open阶段:WebSocket.onopen属性定义一个事件处理程序,当WebSocket 的连接状态readyState 变为1时调用;这意味着当前连接已经准备好发送和接受数据。这个事件处理程序通过 事件(建立连接时)触发
- message:message 事件会在 WebSocket 接收到新消息时被触发
- close:WebSocket.onclose 属性返回一个事件监听器,这个事件监听器将在 WebSocket 连接的readyState 变为 CLOSED时被调用,它接收一个名字为“close”的 CloseEvent 事件
- error:当websocket的连接由于一些错误事件的发生 (例如无法发送一些数据) 而被关闭时,一个error事件将被引发
WebSocket 方法
在ws
中我们常用的有如下两个方法:
- send:使用连接发送数据
- close:关闭连接
HTML中建立ws
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>websocket-demo</title>
</head>
<body>
</body>
<script>
// 建立ws连接
const wbSocket = () => {
// ws实例
let webSocket = null;
// 检测心跳的间隔ID
let intervalID = null;
const connect = async () => {
// 服务端ws的地址
const wsUrl = 'ws://10.199.161.17:9010/ws?deviceId=A51a007F-0620-467B-8A4a-c8a6c9aD69FD&protocolVersion=3'
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
// CLOSED
if (webSocket && webSocket.readyState !== 3) {
return;
}
// Create WebSocket connection.
webSocket = new WebSocket(wsUrl);
// 连接已经准备好发送和接受数据
webSocket.addEventListener("open", (event) => {
webSocket.send("Hello Server, connection has build", event);
});
// Listen for messages
webSocket.addEventListener("message", async (event) => {
console.log("Message from server: ", event.data);
const receivedData = event.data;
if (receivedData instanceof Blob) {
try {
const buffer = await event.data.arrayBuffer()
// 建立DateView对象来读写缓冲区 按照有符号的8位数字读取
const view = new Int8Array(buffer);
// 将类数组view转化为数组,方便读取
const list = Array.from(view)
console.log(list);
} catch (error) {
console.log('解析blob出错', error.message);
}
} else {
console.log('接受到的数据');
}
});
// Listen for possible errors
webSocket.addEventListener("error", (event) => {
console.log("WebSocket error: ", event);
});
webSocket.addEventListener("close", event => {
console.log("socket closed ", event.data);
// 将webSocket 设为Null, 不再发送心跳 等待重新建立连接
clearInterval(intervalID)
webSocket = null;
intervalID = null;
});
}
setInterval(() => {
// 如果有socket实例并且有心跳就直接返回
if (webSocket && webSocket) return
// 无ws实力 or 心跳id则建立ws连接
connect()
}, 5 * 1000)
}
wbSocket()
</script>
</html>
可以看到在上面代码中我们对服务端返回给前端的值做了一层判定,有时服务端在特定的场景下会使用java中的netty这个工具包返回给前端的数据是二进制的,就需要前端判断之后自己再使用arrayBuffer
这个API自己转一下。
如果服务端此时返回的是一个正常的数据而非Blob
的话,那就可以直接在event.data
中获取就可以了。
需要注意一点是ArrayBuffer
是一个表示原始二进制数据的缓冲区,是一个字节数组,并不能直接操作ArrayBuffer
中的内容。需要通过DataView
对象来操作
浏览器中查看ws
如果打印出上面代码中的buffer
的话console.log(111, buffer);
会在浏览器如下显示:
我们点击右侧的那个点之后,会跳转到浏览器的内存检查器,查看对应的值以及存这些值的地址。
参考资料
WebSocket
HTML5 WebSocket
ArrayBuffer