websocket簡介(可跳過)
websocket是一種全雙工通信長鏈接,大多用來實(shí)現(xiàn)及時(shí)通訊,數(shù)據(jù)實(shí)時(shí)性要求較為高的地方,在websoket未出現(xiàn)的時(shí)候前端使用的setInterval輪訓(xùn)進(jìn)行數(shù)據(jù)更新的,在那些對于數(shù)據(jù)實(shí)時(shí)性要求不高地方我們?nèi)钥梢允褂?輪訓(xùn)。
(1)建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易。
(2)與 HTTP 協(xié)議有著良好的兼容性。默認(rèn)端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過各種 HTTP 代理服務(wù)器。
(3)數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。
(4)可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。
(5)沒有同源限制,客戶端可以與任意服務(wù)器通信。
(6)通常的協(xié)議標(biāo)示符號(hào)為http,而websoket是ws(默認(rèn)端口80),加密通信為wss(默認(rèn)端口443)對應(yīng)為https

Http-長短鏈接
HTTP連接分為長連接和短連接,而我們現(xiàn)在常用的都是HTTP1.1,因此我們用的都是長連接。這句話其實(shí)只對了一半,我們現(xiàn)如今的HTTP協(xié)議,大部分都是1.1的,因此我們平時(shí)用的基本上都是長連接。HTTP協(xié)議根本沒有長短連接這一說。HTTP協(xié)議是基于請求/響應(yīng)模式的,因此只要服務(wù)端給了響應(yīng),本次HTTP連接就結(jié)束了,或者更準(zhǔn)確的說,是本次HTTP請求就結(jié)束了,根本沒有長短連接這一說,
TCP-長短鏈接
長連接和短連接,其實(shí)是說的TCP連接。TCP連接是一個(gè)雙向的通道,它是可以保持一段時(shí)間不關(guān)閉的,因此TCP連接才有真正的長連接和短連接。HTTP協(xié)議說到底是應(yīng)用層的協(xié)議,而TCP才是真正的傳輸層協(xié)議,只有負(fù)責(zé)傳輸?shù)倪@一層才需要建立連接。
就像快遞 填的快遞地址單號(hào)是http,而運(yùn)輸工具例如車 對應(yīng)的是tcp
websocket 心跳機(jī)制/斷線重連機(jī)制
當(dāng)雙方通訊都已經(jīng)鏈接,如果沒有外在原因,雙方不斷開鏈接本質(zhì)上鏈接不會(huì)中斷。
但是存在的外在因素會(huì)讓websoket中斷例如:(網(wǎng)絡(luò)斷線,網(wǎng)絡(luò)狀態(tài)不佳,鏈接需要在內(nèi)網(wǎng)而內(nèi)網(wǎng)無法聯(lián)機(jī),鏈接的進(jìn)程被殺死/服務(wù)器長時(shí)間無通訊)
為了保證這個(gè)通信一直保持鮮活 我們需要增加 心跳機(jī)制/斷線重連機(jī)制

心跳機(jī)制
在與服務(wù)器建立起鏈接的時(shí)候(open) 開始心跳
每隔30s(或者指定時(shí)間 這里都暫用30s)向服務(wù)器 send 一條心跳消息 ?等待 服務(wù)器給 答復(fù)
如果在指定30s 前端收到服務(wù)器返回的消息(message), 重置心跳 重新開始計(jì)時(shí)30s 再次心跳。
如果沒有收到消息(可能是網(wǎng)絡(luò)等原因) 服務(wù)器無應(yīng)答 調(diào)用websocke.close() 關(guān)閉這個(gè)長鏈接 再重新發(fā)送新鏈接請求 請求重新連接 服務(wù)器
重連機(jī)制
我們分別在 websokcet 報(bào)錯(cuò)/close/errno/無響應(yīng)(這里心跳直接調(diào)用close,再用close進(jìn)行重新連接)
封裝 具體代碼
調(diào)用方法
import { socketContact } from '@/utils/websocket'
const wss = socketContact('/ws/quality_panel/', 'quality panel data', (data) => {
tableData.value = data // 可以自定義數(shù)據(jù)處理
})
具體封裝代碼:
const socketContact = function (contactUrl = '', getDataSecret = '', getDataFn = () => {}) {
const baseUrl = process.env.VUE_APP_API_WEBSOCKET_URL // baseURL
const callBack = getDataFn // callback返回?cái)?shù)據(jù)函數(shù)
const url = contactUrl // 后綴url
// getDataSecret 需要發(fā)送給服務(wù)器的數(shù)據(jù)
let socket // websocket本體
let lock = false // 避免重復(fù)連接
let replyOverTime = null // 服務(wù)器回復(fù)超時(shí)setTimeout
let hearByteTime = null // 心跳 setTimeout
try {
socket = new WebSocket(baseUrl + url)
socket.addEventListener('open', function (event) {
socket.send(getDataSecret)
heatByte() // 開始心跳
console.log('鏈接成功', contactUrl, getDataSecret)
})
socket.addEventListener('message', function (event) {
resetHardByte() // 重置心跳
// 這里transporting...' 和'invalid command...'為服務(wù)器心跳回復(fù) 不做數(shù)據(jù)返回處理
if (event.data === 'transporting...' || event.data === 'invalid command...') return
// 處理服務(wù)器返回?cái)?shù)據(jù) 這里可自定義
const str = event.data.replaceAll('None', 'null')
// eslint-disable-next-line no-eval
const data = eval('(' + str + ')')
callBack(data)
});
socket.addEventListener('close', function (event) {
console.log('鏈接關(guān)閉', event.code)
reconnect(url, getDataSecret, callBack) // 嘗試重新連接
});
socket.addEventListener('errno', function (event) {
console.log('鏈接報(bào)錯(cuò)')
reconnect(url, getDataSecret, callBack)// 嘗試重新連接
});
} catch (e) {
console.log('catch', e)
reconnect(url, getDataSecret, callBack) // 嘗試重新連接
}
function heatByte (type) {
hearByteTime && clearInterval(hearByteTime)
replyOverTime && clearInterval(replyOverTime)
// 每30s一次心跳
hearByteTime = setTimeout(() => {
if (socket.readyState === 1) {
socket.send('heart byte') // 沒問題進(jìn)行心跳
console.log('heart byte')
}
// 重新鏈接超時(shí) 60s無回復(fù) 關(guān)閉連接 嘗試重新鏈接
replyOverTime = setTimeout(() => {
socket.close()
console.log('網(wǎng)絡(luò)/其他問題 回復(fù)超時(shí)間關(guān)閉')
}, 60000)
}, 30000)
}
function resetHardByte () {
// 進(jìn)行下一次心跳
hearByteTime && clearTimeout(hearByteTime)
replyOverTime && clearTimeout(replyOverTime)
heatByte()
}
function reconnect (url, getDataSecret, callBack) {
if (lock) return
lock = true
setTimeout(() => {
lock = false
socketContact(url, getDataSecret, callBack)
console.log('正在嘗試重新連接')
}, 5000)
}
return socket
}
export { socketContact }
參考
長鏈接短連接:http://t.csdn.cn/Ne7g3
websokcet 阮一峰:https://www.ruanyifeng.com/blog/2017/05/websocket.html文章來源:http://www.zghlxwxcb.cn/news/detail-501434.html
websokcet:http://t.csdn.cn/j42h5文章來源地址http://www.zghlxwxcb.cn/news/detail-501434.html
到了這里,關(guān)于websocket前端封裝代碼,心跳機(jī)制斷線重連的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!