WebSocket中心跳檢測(cè)與斷開重連機(jī)制詳解
心跳檢測(cè)
在長(zhǎng)時(shí)間WebSocket
連接過程中,容易因?yàn)榫W(wǎng)絡(luò)或其他原因?qū)е?code>WebSocket斷開而沒有觸發(fā)ws.onclose
事件,導(dǎo)致無法獲取WebSocket
連接失敗的狀態(tài)。
利用心跳檢測(cè)可以預(yù)防這一問題。心跳檢測(cè)是指在WebSocket
連接過程中定時(shí)向服務(wù)端發(fā)送和接收心跳消息,來確定當(dāng)前連接是否是正常狀態(tài)的檢測(cè)機(jī)制。
斷開重連
斷開重連是指在WebSocket
不正常斷開連接后,進(jìn)行重新連接的策略。
在WebSocket
連接斷開后,立馬進(jìn)行重新連接。如果連接失敗,則間隔一定時(shí)間后再嘗試重連,直到連接成功或者達(dá)到最大重連次數(shù)。
消息訂閱
在WebSocket
使用過程中,可能會(huì)在不同的頁面或邏輯中需要訂閱全部或部分WebSocket
消息,利用消息訂閱可以讓使用者在不同頁面中訂閱獲取相關(guān)的消息信息。
代碼實(shí)現(xiàn)
定義WebSocket內(nèi)部參數(shù)
this.url = url //WebSocket連接地址 this.ws = null //WebSocket連接對(duì)象 this.heartEnabled = heartEnabled //是否開啟心跳 this.heartInterval = 10000 //心跳間隔時(shí)間 this.heartTimeout = 5000 //心跳超時(shí)時(shí)間 this.lockReconnect = false //是否禁止重連 this.heartTimer = null //心跳定時(shí)器 this.serverTimer = null //服務(wù)器超時(shí)定時(shí)器 this.reconnectCount = 0 //重連次數(shù) this.maxReconnectCount = 5 //最大重連次數(shù) this.observers = [] //消息訂閱者列表 this.waitingMessages = [] //待執(zhí)行命令列表
實(shí)現(xiàn)WebSocket連接
//WebSocket連接 connect() { this.ws = new WebSocket(this.url) this.ws.onopen = () => { this.reconnectCount = 0 // 重置重連次數(shù) this.heartEnabled && this.start() // 開啟心跳 // 發(fā)送所有等待發(fā)送的信息 const length = this.waitingMessages.length for (let i = 0; i < length; ++i) { const message = this.waitingMessages.shift() this.send(message) } } this.ws.onclose = (event) => { console.log('WebSocket closed:', event) this.reconnect() //這里可根據(jù)具體情況判定是否重新連接 } this.ws.onerror = (error) => { console.log('WebSocket error:', error) this.reconnect() } this.ws.onmessage = (event) => { //收到心跳信息則重置心跳,收到其他信息則觸發(fā)回調(diào) if (event.data === 'pong') { this.start() } else { this.observers.forEach((observer) => { //如需要,這里可根據(jù)observer的訂閱type來判斷是否回調(diào) observer.callback(event.data) }) } } }
消息訂閱及發(fā)送
//發(fā)送信息 send(message) { //發(fā)送信息時(shí)若WebSocket還未連接,則將信息放入待發(fā)送信息中等待連接成功后發(fā)送 if (this.onReady() !== WebSocket.OPEN) { this.waitingMessages.push(message) return this } this.ws.send(message) return this } //訂閱WebSocket信息 onObserve(callback, type = 'all') { const observer = { type, callback } this.observers.push(observer) return observer } //取消訂閱信息 cancelObserve(cancelObserver) { this.observers.forEach((observer, index) => { if (cancelObserver === observer) { this.observers.splice(index, 1) } }) } //WebSocket連接狀態(tài) onReady() { return this.ws.readyState }
心跳檢測(cè)
//開啟心跳 start() { this.reset() this.heartTimer = setTimeout(() => { this.send('ping') //達(dá)到心跳超時(shí)時(shí)間還沒有返回心跳信息,則認(rèn)為連接斷開,關(guān)閉WebSocket并重連 this.serverTimer = setTimeout(() => { this.ws.close() }, this.heartTimeout) }, this.heartInterval) } //重置心跳定時(shí)器/服務(wù)超時(shí)定時(shí)器 reset() { this.heartTimer && clearTimeout(this.heartTimer) this.serverTimer && clearTimeout(this.serverTimer) }
斷開重連
//重連 reconnect() { // 設(shè)置lockReconnect變量避免重復(fù)連接 if (this.lockReconnect || this.reconnectCount >= this.maxReconnectCount) return this.lockReconnect = true this.reconnectCount++ //重連次數(shù)+1 setTimeout(() => { this.connect() this.lockReconnect = false }, 1000 * this.reconnectCount) //重連次數(shù)越多,延時(shí)越久 }
完整代碼 js
class SocketConnect { constructor(url = 'ws://127.0.0.1:8080', heartEnabled = true) { this.url = url //WebSocket連接地址 this.ws = null //WebSocket連接對(duì)象 this.heartEnabled = heartEnabled //是否開啟心跳 this.heartInterval = 10000 //心跳間隔時(shí)間 this.heartTimeout = 5000 //心跳超時(shí)時(shí)間 this.lockReconnect = false //是否禁止重連 this.heartTimer = null //心跳定時(shí)器 this.serverTimer = null //服務(wù)器超時(shí)定時(shí)器 this.reconnectCount = 0 //重連次數(shù) this.maxReconnectCount = 5 //最大重連次數(shù) this.observers = [] //消息訂閱者列表 this.waitingMessages = [] //待執(zhí)行命令列表 this.connect() } //WebSocket連接 connect() { this.ws = new WebSocket(this.url) this.ws.onopen = () => { this.reconnectCount = 0 // 重置重連次數(shù) this.heartEnabled && this.start() // 開啟心跳 // 發(fā)送所有等待發(fā)送的信息 const length = this.waitingMessages.length for (let i = 0; i < length; ++i) { const message = this.waitingMessages.shift() this.send(message) } } this.ws.onclose = (event) => { console.log('WebSocket closed:', event) this.reconnect() } this.ws.onerror = (error) => { console.log('WebSocket error:', error) this.reconnect() } this.ws.onmessage = (event) => { //收到心跳信息則重置心跳,收到其他信息則觸發(fā)回調(diào) if (event.data === 'pong') { this.start() } else { this.observers.forEach((observer) => { observer.callback(event.data) }) } } } //發(fā)送信息 send(message) { //發(fā)送信息時(shí)若WebSocket還未連接,則將信息放入待發(fā)送信息中等待連接成功后發(fā)送 if (this.onReady() !== WebSocket.OPEN) { this.waitingMessages.push(message) return this } this.ws.send(message) return this } //訂閱webSocket信息 onObserve(callback, type = 'all') { const observer = { type, callback } this.observers.push(observer) return observer } //取消訂閱信息 cancelObserve(cancelObserver) { this.observers.forEach((observer, index) => { if (cancelObserver === observer) { this.observers.splice(index, 1) } }) } //開啟心跳 start() { this.reset() this.heartTimer = setTimeout(() => { this.send('ping') //5秒鐘還沒有返回心跳信息,則認(rèn)為連接斷開,關(guān)閉WebSocket并重連 this.serverTimer = setTimeout(() => { this.ws.close() }, this.heartTimeout) }, this.heartInterval) } //重置心跳定時(shí)器/服務(wù)超時(shí)定時(shí)器 reset() { this.heartTimer && clearTimeout(this.heartTimer) this.serverTimer && clearTimeout(this.serverTimer) } //重連 reconnect() { // 設(shè)置lockReconnect變量避免重復(fù)連接 if (this.lockReconnect || this.reconnectCount >= this.maxReconnectCount) return this.lockReconnect = true this.reconnectCount++ //重連次數(shù)+1 setTimeout(() => { this.connect() this.lockReconnect = false }, 1000 * this.reconnectCount) //重連次數(shù)越多,延時(shí)越久 } //WebSocket連接狀態(tài) onReady() { return this.ws.readyState } } export default SocketConnect
使用示例
// WebSocket連接 const url = 'ws://127.0.0.1:8080' const ws = new SocketConnect(url) // 消息訂閱 const observer = ws.onObserve((data) => { console.log('data:', data) }) // 取消訂閱 ws.cancelObserve(observer) // 發(fā)送消息 ws.send('hello world')
完整代碼 ts
type ObserverType = { type: string callback: (event: MessageEvent) => void } type MessageDataType = object class SocketConnect { private url: string private ws: WebSocket | undefined //WebSocket實(shí)例 private heartEnabled: boolean //是否開啟心跳 private heartInterval = 10000 //心跳間隔時(shí)間 private heartTimeout = 5000 //心跳超時(shí)時(shí)間 private lockReconnect = false //是否禁止重連 private heartTimer: NodeJS.Timeout | undefined //心跳定時(shí)器 private serverTimer: NodeJS.Timeout | undefined //服務(wù)器超時(shí)定時(shí)器 private reconnectCount = 0 //重連次數(shù) private maxReconnectCount = 5 //最大重連次數(shù) private observers: ObserverType[] = [] //消息訂閱者列表 private waitingMessages: string[] = [] //待執(zhí)行命令列表 constructor(url = 'ws://127.0.0.1:8080', heartEnabled = false) { this.url = url this.heartEnabled = heartEnabled this.connect() } //WebSocket連接 connect() { this.ws = new WebSocket(this.url) this.ws.onopen = () => { this.reconnectCount = 0 // 重置重連次數(shù) this.heartEnabled && this.start() // 開啟心跳 // 發(fā)送所有等待發(fā)送的信息 const length = this.waitingMessages.length for (let i = 0; i < length; ++i) { const message = this.waitingMessages.shift() this.send(message) } } this.ws.onclose = (event) => { console.log('WebSocket closed:', event) this.reconnect() } this.ws.onerror = (error) => { console.log('WebSocket error:', error) this.reconnect() } this.ws.onmessage = (event: MessageEvent) => { //收到心跳信息則重置心跳,收到其他信息則觸發(fā)回調(diào) if (event.data === 'pong') { this.start() } else { this.observers.forEach((observer) => { observer.callback(event.data) }) } } } //發(fā)送信息 send(message?: string) { if (message) { //發(fā)送信息時(shí)若WebSocket還未連接,則將信息放入待發(fā)送信息中等待連接成功后發(fā)送 if (this.onReady() !== WebSocket.OPEN) { this.waitingMessages.push(message) return this } this.ws && this.ws.send(message) } return this } //訂閱WebSocket信息 onObserve(callback: (data: MessageDataType) => void, type = 'all') { const observer = { type, callback } this.observers.push(observer) return observer } //取消訂閱信息 cancelObserve(cancelObserver: ObserverType) { this.observers.forEach((observer, index) => { if (cancelObserver === observer) { this.observers.splice(index, 1) } }) } //開啟心跳 private start() { this.reset() this.heartTimer = setTimeout(() => { this.send('ping') //5秒鐘還沒有返回心跳信息,則認(rèn)為連接斷開,關(guān)閉WebSocket并重連 this.serverTimer = setTimeout(() => { this.ws && this.ws.close() }, this.heartTimeout) }, this.heartInterval) } //重連 private reconnect() { // 設(shè)置lockReconnect變量避免重復(fù)連接 if (this.lockReconnect || this.reconnectCount >= this.maxReconnectCount) return this.lockReconnect = true this.reconnectCount++ //重連次數(shù)+1 setTimeout(() => { this.connect() this.lockReconnect = false }, 1000 * this.reconnectCount) //重連次數(shù)越多,延時(shí)越久 } // 重置心跳定時(shí)器/服務(wù)超時(shí)定時(shí)器 private reset() { this.heartTimer && clearTimeout(this.heartTimer) this.serverTimer && clearTimeout(this.serverTimer) } // WebSocket連接狀態(tài) onReady() { return this.ws && this.ws.readyState } } export default SocketConnect
以上就是WebSocket中心跳檢測(cè)與斷開重連機(jī)制詳解的詳細(xì)內(nèi)容,更多關(guān)于WebSocket心跳檢測(cè)與斷開重連的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript firefox不顯示本地預(yù)覽圖片問題的解決方法
在Firefox一直不能用js做出圖片預(yù)覽的效果,下面這個(gè)即可解決,用替換的方法實(shí)現(xiàn)firefox支持得的路徑格式2008-11-11Chrome不支持showModalDialog模態(tài)對(duì)話框和無法返回returnValue問題的解決方法
上個(gè)禮拜修改測(cè)試一個(gè)后臺(tái)管理項(xiàng)目,在測(cè)試與各個(gè)瀏覽器兼容性的時(shí)候,發(fā)現(xiàn)在chrome瀏覽器下showModalDialog方法顯示的并不是模態(tài)對(duì)話框,就像新打開一個(gè)頁面一樣,父窗口仍然可以隨意獲取焦點(diǎn),并可以打開多個(gè)窗體,而且返回值returnValue也無法返回,一直是undefined2016-10-10JavaScript函數(shù)式編程(Functional Programming)高階函數(shù)(Higher order fun
這篇文章主要介紹了JavaScript函數(shù)式編程(Functional Programming)高階函數(shù)(Higher order functions),結(jié)合實(shí)例形式分析了javascript函數(shù)式編程高級(jí)函數(shù)的概念、原理、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-05-05Bootstrap Table 雙擊、單擊行獲取該行及全表內(nèi)容
這篇文章主要介紹了Bootstrap Table 雙擊、單擊行獲取該行內(nèi)容及獲取全表的內(nèi)容,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08微信小程序中的video視頻實(shí)現(xiàn) 自定義播放按鈕、封面圖、視頻封面上文案
這篇文章主要介紹了微信小程序中的video視頻實(shí)現(xiàn) 自定義播放按鈕、封面圖、視頻封面上文案,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01javascript刪除html標(biāo)簽函數(shù)cIsHTML
這篇文章主要介紹了javascript刪除html標(biāo)簽函數(shù)cIsHTML,需要的朋友可以參考下2017-01-01ECMAScript6塊級(jí)作用域及新變量聲明(let)
這篇文章主要介紹了ECMAScript6塊級(jí)作用域及新變量聲明(let) 的相關(guān)資料,需要的朋友可以參考下2015-06-06JS實(shí)現(xiàn)仿京東淘寶豎排二級(jí)導(dǎo)航
本文給大家分享一段使用原生Javascript實(shí)現(xiàn)的仿京東淘寶豎排二級(jí)導(dǎo)航的代碼,非常的實(shí)用,有需要的小伙伴參考下2014-12-12