聊聊websocket的那些事儿🤩
废话不多说,直接进入正题吧~
什么是websocket
websocket 翻译过来就是 web套接字,用于给浏览器提供与服务器进行 双向通信 的能力,与HTTP不同,它以 ws://或wss:// 开头,它是一个有状态协议,这意味着客户端和服务器之间的连接将保持活动状态,即 长连接,连接的断开可以由浏览器或者服务器发起
浏览器里提供了 WebSocket 构造函数,用于创建WebSocket连接,当我们使用 new WebSocket(url,[protocol]) 后,便可以得到一个WebSocket实例,该实例上有如下的属性、方法和监听事件
- protocol:是个只读属性,用于返回服务器端选中的子协议的名字,这是一个在创建WebSocket对象时,在参数[protocols]中指定的字符串,当没有已建立的链接时为空串
- readyState:返回当前WebSocket实例的连接状态,是个只读属性,分别有如下取值
- 0:正在连接中
- 1:已经连接并且可以通讯
- 2:连接正在关闭
- 3:连接已关闭或者没有连接成功
- bufferedAmount:只读属性,用于返回已经被send方法放入队列中但还没有被发送到网络中的数据的字节数,一旦队列中的所有数据被发送至网络,则该属性值将被重置为 0,但是,若在发送过程中连接被关闭,则属性值不会重置为0
- url:只读属性,返回值为构造函数创建WebSocket实例时传入的url
- close方法:关闭当前连接
- send方法:通过该连接发送数据,该方法将需要通过 WebSocket 连接传输至服务器的数据 排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount 的值。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭,需要注意的是,发送的数据格式只支持如下几种
- USVString:文本字符串,字符串将以 UTF-8 格式添加到缓冲区,并且 bufferedAmount 将加上该字符串以 UTF-8 格式编码时的字节数的值
- ArrayBuffer:使用有类型的数组对象发送底层二进制数据;其二进制数据内存将被缓存于缓冲区,bufferedAmount 将加上所需字节数的值
- Blob:将队列 blob 中的原始数据以二进制传输,bufferedAmount 将加上原始数据的字节数的值
- ArrayBufferView:以二进制帧的形式发送任何JavaScript类数组对象,其二进制数据内容将被队列于缓冲区中,bufferedAmount 将加上必要字节数的值
- close事件:用于指定连接关闭后的回调函数
- error事件:用于指定连接失败后的回调函数
- message事件:用于指定当从服务器接收到数据时的回调函数
- open事件:用于指定连接成功后的回调函数
下面是一个简单的websocket使用示例
const socket = new WebSocket('ws://localhost:8080');
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
实战经验
当然如果我们想要实际在项目中去使用websocket,光是知道上述的一些基础概念是不够的,所以本节会给出一些在实战中的使用经验
心跳检测
我们知道了websocket是一个 长连接,因此就必须要有 鉴活机制,来保证通信信道的健康,整体的鉴活流程大致如下图
代码示例如下
let socket,heartBeatTimeout
const sendHeatBeat = ()=>{
//如果超过十秒未收到心跳响应消息,则重新建立连接
socket.send('heartBeat-request')
heartBeatTimeout = setTimeout(()=>{
initWs()
},10000)
}
const onOpen = ()=>{
sendHeatBeat()
}
const onMessage = e => {
if(e.data === 'heartBeat-response') {
//收到心跳响应消息,则重新计时
clearTimeout(heartBeatTimeout)
heartBeatTimeout = null
sendHeatBeat()
}
}
const onError = () => {
//连接失败后,需要重新建立连接
initWs()
}
const destroyWs = ()=>{
if(socket) {
socket.close()
socket.removeEventListener('open',onOpen)
socket.removeEventListener('message',onMessage)
socket.removeEventListener('error',onError)
clearTimeout(heartBeatTimeout)
heartBeatTimeout = null
}
}
const initWs = ()=>{
destroyWs()
socket = new WebSocket('ws://localhost:8080');
socket.addEventListener('open',onOpen)
socket.addEventListener('message',onMessage)
socket.addEventListener('error',onError)
}
initWs()
消息处理
在使用 http 的场景下,我们的交互产生的请求与响应是一一对应的,因此我们是能知道响应的数据作用于界面哪一部分的,而在websocket的场景下,从服务器推送过来的消息,默认情况下我们并不知道是对应哪一次请求或者说哪一次交互,从而没法知道返回的数据是需要如何使用的
因此,在websocket的场景下,响应消息的格式 尤为重要,在进行前后端联调时,一定要规范好数据格式,这样才能为后续的交互铺好路,于我看来,一个理想的消息格式如下
{
type // 用于标识该消息是用来干啥的
parmas // 请求携带的参数,通过响应返回过来,可用于一些业务操作
response // 业务数据
}
其中跟http响应最大的不同是 websocket 的消息里必须要有一个类似type的字段,用于告诉我们这个消息是用来干啥的,从而我们才能正确地使用这个消息来做页面更新或其他处理,本质上是借助类似type字段来实现 请求与消息的绑定,即实现一对一的对应关系
结语
虽然websocket给我们提供了双向通信的能力,但是如果我们不能正确处理由此带来的一些副作用,那么肯定会出问题的,因此如果要想使用它,程序的复杂度也会随之提升,但我相信通过本文的阅读,你一定可以熟练运用websocket了😉,加油!
都看到这里啦,如果本篇文章对你有帮助,希望能 点个赞👍 支持下啦,你们的支持才是我最大的动力!😘
转载自:https://juejin.cn/post/7232209767593754682