websocket 实战(附源码)
写在前面
本文不会去分析
websocket
与http
接口的区别,也不会写太多基础知识的介绍。只介绍websocket
在前端开发中的实际应用(附源码)
websocket
作为全双工通信工具,在 web 开发中,用于前后端之间的消息推送、实时通信;这一点是 http
接口无法代替的(因为 websocket
是更优雅,性能更好的方式)
其实 websocket
是简单的。但是,很多人在第一次使用它的时候,会有很多疑问以及无从下手的问题。
下面开始 websocket
在项目中的使用。
创建连接
var ws = new WebSocket(url, [protocol])
第一个参数 url
,指定连接的 URL。第二个参数 protocol
是可选的,指定了可接受的子协议。
例如:
let url = `${process.env['VUE_APP_WEBSOCKET']}/}/websocket`
let ws = new WebSocket(url)
即可拿到一个 ws
实例。
监听事件
ws.addEventListener('open', e => {
console.log('长连接连接成功')
// 连接成功后的回调函数,连接成功后一般需要开启心跳方法
clearTimeout(timer.reconnectObj)
dispatch('wsHeartStart')
})
ws.addEventListener('message', e => {
// 接收服务端数据时触发的回调函数
dispatch('wsHearReset')
dispatch('handlerWSMessage', e)
})
ws.addEventListener('close', e => {
// 通信发生错误时触发
dispatch('handlerWSClose', e)
})
ws.addEventListener('error', e => {
// 连接关闭时触发
dispatch('handlerWSError', e)
})
属性&方法
Websocket
有哪些属性和方法呢?
属性
-
wx.readyState
readyState
表示连接状态,=> 状态常量介绍 -
wx.bufferedAmount
方法
-
ws.send(data)
// 发送数据 -
ws.close([code[, reason]])
// 关闭连接
心跳
心跳 是 websocket
中的一个概念,就是在 websocket
中用定时器,定时向服务器发送一个消息内容,我们约定,如果后端收到这个消息内容,就要向前端回一个消息内容。如果前端没有收到消息回复,则有可能 websocket
断掉了,那么就要进行重连操作,来保证我们的 websocket
处于连接状态。
let url = `${process.env['VUE_APP_WEBSOCKET']}/}/websocket`
let ws = new WebSocket(url)
ws.addEventListener('open', e => {
console.log('长连接连接成功')
// 执行心跳方法
dispatch('wsHeartStart')
})
websocket
链接成功以后,开启心跳方法。心跳方法代码如下:
// 开启心跳
wsHeartStart({ state, dispatch }) {
timer.wsTimeoutObj = setTimeout(() => {
// 我们这里向后端发送一个空串
state.ws.send('')
timer.serverTimeoutObj = setTimeout(() => {
// 如果在规定时间内没有收到心跳回传,则判定为 websocket error
// 在 websocket error 中,我们会执行重连操作
dispatch('handlerWSError')
}, state.waitHeartTime * 1000)
}, state.wsTimeout * 1000)
}
要点总结
-
WebSocket
协议本质上是一个基于 TCP 的协议。为了建立一个WebSocket
连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"
表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的WebSocket
连接就建立起来了,双方就可以通过这个连接通道*的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
- 软件通信有七层结构,下三层结构偏向与数据通信,上三层更偏向于数据处理,中间的传输层则是连接上三层与下三层之间的桥梁,每一层都做不同的工作,上层协议依赖于下层协议。基于这个通信结构的概念。
Socket
其实并不是一个协议,是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。当两台主机通信时,让Socket
去组织数据,以符合指定的协议。TCP 连接则更依靠于底层的 IP 协议,IP 协议的连接则依赖于链路层等更低层次。WebSocket
则是一个典型的应用层协议。总的来说:Socket 是传输控制层协议,WebSocket 是应用层协议。
问题思考
- 如何做鉴权操作,例如
ws
请求中添加自定义headers
?
扩展知识
1、SSE(Server-sent Events)
SSE(Server-sent Events)是 WebSocket
的一种轻量代替方案,使用 HTTP 协议。。Server-Sent Events 教程
2、课外阅读:WebSocket 是什么原理?为什么可以实现持久连接?
源代码
import { EventLsbridge } from '@/common/eventLsbridge'
const timer = {
reconnectObj: null,
wsTimeoutObj: null,
serverTimeoutObj: null,
// 重连时间间隔(s)
reconnectTime: 3
}
const state = {
ws: null,
// 心跳时间(s)
wsTimeout: 50,
// 等待心跳响应时间(s)
waitHeartTime: 10
}
const mutations = {
set_reconnectObj: (state, val) => {
state.reconnectObj = val
},
}
const actions = {
// 心跳重置
wsHearReset({ dispatch }) {
clearTimeout(timer.wsTimeoutObj)
clearTimeout(timer.serverTimeoutObj)
dispatch('wsHeartStart')
},
// 开启心跳
wsHeartStart({ state, dispatch }) {
timer.wsTimeoutObj = setTimeout(() => {
state.ws.send('')
timer.serverTimeoutObj = setTimeout(() => {
dispatch('handlerWSError')
}, state.waitHeartTime * 1000)
}, state.wsTimeout * 1000)
},
// 初始化ws连接
initWebsocket({ dispatch, state }) {
if (state.ws) {
return
} else {
return new Promise(resolve => {
let url = `${process.env['VUE_APP_WEBSOCKET']}/websocket`
state.ws = ws
state.ws.addEventListener('open', e => {
console.log('长连接连接成功')
clearTimeout(timer.reconnectObj)
dispatch('wsHeartStart')
})
state.ws.addEventListener('message', e => {
dispatch('wsHearReset')
dispatch('handlerWSMessage', e)
})
state.ws.addEventListener('close', e => {
dispatch('handlerWSClose', e)
})
state.ws.addEventListener('error', e => {
dispatch('handlerWSError', e)
})
})
}
},
handlerSend({state}, id = -1) {
if(!state.ws) {
return
}
state.ws.send(
JSON.stringify({
id
})
)
},
handlerWSClose({ state }, e) {
state.ws?.close()
state.ws = null
clearTimeout(timer.wsTimeoutObj)
clearTimeout(timer.serverTimeoutObj)
clearTimeout(timer.reconnectObj)
},
handlerWSError({state, dispatch}, e) {
state.ws = null
dispatch('reconnect')
},
reconnect({state, commit, dispatch}) {
clearTimeout(timer.wsTimeoutObj)
clearTimeout(timer.serverTimeoutObj)
clearTimeout(timer.reconnectObj)
timer.reconnectObj = window.setTimeout(() => {
dispatch('initWebsocket')
timer.reconnectTime += 4
if(timer.reconnectTime > 60) {
timer.reconnectTime = 20
}
}, timer.reconnectTime * 1000)
},
// ws回调
handlerWSMessage({state}, e) {
let respData = {}
try {
respData = JSON.parse(e.data)
} catch (e) {
// not todo
}
// 处理-服务端推送过来的数据
if (respData?.data) {
EventLsbridge.vueEmit(EventLsbridge.eventName.wsData, respData)
}
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
-----------------(正文完)------------------
前端学习交流群,想进来面基的,可以加群: 685486827,832485817;
写在最后: 约定优于配置 —— 软件开发的简约原则
--------------------------------(完)--------------------------------------
我的:
个人网站: https://neveryu.github.io/neveryu/
Github: https://github.com/Neveryu
新浪微博: https://weibo.com/Neveryu
微信: miracle421354532
更多学习资源请关注我的新浪微博…好吗