webSocket通信封裝 一. js使用WebSocket介紹 二. webSocket js封裝使用 三. js封裝代碼 一. js使用WebSocket介紹 1. 創(chuàng)建WebSocket實(shí)例 url:url之前需添加ws://(未加密)或wss://(已加密),類似http://、https:// protocol:與服務(wù)端定義的協(xié)議名稱相同,協(xié)議的參數(shù)例如XMPP(Extensible Messaging and Presence Protocol)、SOAP(Simple Object Access Protocol)或者自定義協(xié)議 var ws = new WebSocket('ws://url'); var ws1 = new WebSocket('ws://url', 'myprotocol'); var ws2 = new WebSocket('ws://url', ['protocol_1','protovol_2'])); 1 2 3 2.屬性 ①readyState屬性:WebSocket當(dāng)前連接狀態(tài) 屬性值 屬性常量 描述 0 CONNECTING 正在與服務(wù)端建立WebSocket連接,還沒有連接成功 1 OPEN 連接成功并打開,可以發(fā)送消息 2 CLOSING 進(jìn)行關(guān)閉連接的操作,且尚未關(guān)閉 3 CLOSE 連接已關(guān)閉或不能打開 通過 ws.readyState屬性查看當(dāng)前連接狀態(tài) alert('ws連接狀態(tài):' + ws.readyState); 1 ②bufferedAmount:檢查傳輸數(shù)據(jù)的大小,當(dāng)客戶端傳輸大量數(shù)據(jù)時(shí)使用避免網(wǎng)絡(luò)飽和 ③protocol:在構(gòu)造函數(shù)中使用,protocol參數(shù)讓服務(wù)端知道客戶端使用的WebSocket協(xié)議。而WebSocket對(duì)象的這個(gè)屬性就是指的最終服務(wù)端確定下來的協(xié)議名稱,可以為空 3. 方法 ①發(fā)送數(shù)據(jù):send() ②關(guān)閉連接:closed() //發(fā)送數(shù)據(jù) var message = { id: 1, title: '發(fā)送ws數(shù)據(jù)' } ws.send(JSON.stringify(message)); // 復(fù)雜的數(shù)據(jù)結(jié)構(gòu)要先進(jìn)行序列化 //關(guān)閉連接 ws.close() 4. 事件 WebSocket API是純事件驅(qū)動(dòng),建立連接之后,可自動(dòng)發(fā)送狀態(tài)改變的數(shù)據(jù)和通知 事件值 描述 onopen 當(dāng)建立websocket連接時(shí)觸發(fā),只觸發(fā)一次 onerror 當(dāng)連接出現(xiàn)錯(cuò)誤時(shí)觸發(fā)-因?yàn)楫?dāng)觸發(fā)了onerror之后連接就會(huì)觸發(fā)關(guān)閉事件 onmessage 當(dāng)服務(wù)端發(fā)送數(shù)據(jù)時(shí)觸發(fā),可多次觸發(fā),頁面數(shù)據(jù)展示處理模塊–實(shí)現(xiàn)輪詢 onclose 當(dāng)websocket連接關(guān)閉時(shí)觸發(fā),只觸發(fā)一次 使用示例 var ws = new WebSocket('ws://url'); // 獲取連接狀態(tài) console.log('ws連接狀態(tài):' + ws.readyState); //監(jiān)聽是否連接成功 ws.onopen = function () { console.log('ws連接狀態(tài):' + ws.readyState); //連接成功則發(fā)送一個(gè)數(shù)據(jù) ws.send('test1'); } // 接聽服務(wù)器發(fā)回的信息并處理展示 ws.onmessage = function (data) { console.log('接收到來自服務(wù)器的消息:'); console.log(data); //完成通信后關(guān)閉WebSocket連接 ws.close(); } // 監(jiān)聽連接關(guān)閉事件 ws.onclose = function () { // 監(jiān)聽整個(gè)過程中websocket的狀態(tài) console.log('ws連接狀態(tài):' + ws.readyState); } // 監(jiān)聽并處理error事件 ws.onerror = function (error) { console.log(error); } 二. webSocket js封裝使用 創(chuàng)建一個(gè)js文件, 代碼在下, 最后將方法暴露出去, 使用的時(shí)候可以直接引用文件中的方法 調(diào)用, 或者 將該js文件掛載在vue的prototype中 直接引用文件 方法 調(diào)用 import {createWebSocket} from '@/utils/socket’ createWebSocket() 掛載在vue的prototype中 3. 整個(gè)項(xiàng)目只有一條webSocket通信: ① 在登錄后即可創(chuàng)建webSocket通信, 接受的數(shù)據(jù)中可定義一個(gè)參數(shù), 該參數(shù)來控制是 做什么功能 例如這里 : “webSocket_device_transport” 是某一個(gè)界面需要實(shí)時(shí)添加的一些數(shù)據(jù) “webSocket_device_alarm” 是一個(gè)全局提示彈框, 需要添加的數(shù)據(jù) ② 創(chuàng)建了 new Map(); key和value 用來一一對(duì)應(yīng)發(fā)送和接受的數(shù)據(jù) //思路 var globalCallback = new Map(); //創(chuàng)建 new Map() globalCallback.set(key,callback); //發(fā)送數(shù)據(jù)的時(shí)候, 要定義一個(gè)唯一的key 和后端返回的key是一一對(duì)應(yīng), 定義一個(gè)回調(diào)函數(shù)用來接收數(shù)據(jù)后處理數(shù)據(jù) //例如上圖片中 接收的數(shù)據(jù) res.method == "webSocket_device_transport", 用sn作為唯一的標(biāo)識(shí)符, // 客戶端在發(fā)送數(shù)據(jù)時(shí) globalCallback.set("sn",resultFunc), 通過globalCallback存了一個(gè)key為"sn"的回調(diào)函數(shù), // 然后通信接收的數(shù)據(jù)中 返回作為標(biāo)識(shí)符的"sn", 用來查找存的回調(diào)函數(shù), 然后處理數(shù)據(jù). (因?yàn)樘幚頂?shù)據(jù)的方法可能有多種, 不唯一, 所以用回調(diào)函數(shù)) const callback = globalCallback.get(ret.sn) callback(ret); 三. js封裝代碼 import { getToken} from '@/utils/auth' var websock = null; let rec; //斷線重連后,延遲5秒重新創(chuàng)建WebSocket連接 rec用來存儲(chǔ)延遲請(qǐng)求的代碼 let isConnect = false; //連接標(biāo)識(shí) 避免重復(fù)連接 let checkMsg = "heartbeat"; //心跳發(fā)送/返回的信息 服務(wù)器和客戶端收到的信息內(nèi)容如果如下 就識(shí)別為心跳信息 不要做業(yè)務(wù)處理 var globalCallback = new Map(); let createWebSocket = () => { try { initWebSocket(); //初始化websocket連接 } catch (e) { console.log("嘗試創(chuàng)建連接失敗"); reConnect(); //如果無法連接上webSocket 那么重新連接!可能會(huì)因?yàn)榉?wù)器重新部署,或者短暫斷網(wǎng)等導(dǎo)致無法創(chuàng)建連接 } }; //定義重連函數(shù) let reConnect = () => { console.log("嘗試重新連接"); if (isConnect) return; //如果已經(jīng)連上就不在重連了 rec && clearTimeout(rec); rec = setTimeout(function () { // 延遲5秒重連 避免過多次過頻繁請(qǐng)求重連 createWebSocket(); }, 5000); }; //設(shè)置關(guān)閉連接 let closeWebSocket = () => { websock.close(); }; //心跳設(shè)置 var heartCheck = { timeout: 20000, //每段時(shí)間發(fā)送一次心跳包 這里設(shè)置為20s timeoutObj: null, //延時(shí)發(fā)送消息對(duì)象(啟動(dòng)心跳新建這個(gè)對(duì)象,收到消息后重置對(duì)象) start: function () { this.timeoutObj = setInterval(function () { console.log("hearting ....") if (isConnect) websock.send(checkMsg); }, this.timeout); }, reset: function () { clearInterval(this.timeoutObj) this.start(); }, stop: function (){ clearInterval(this.timeoutObj) } }; // 初始化websocket function initWebSocket() { // ws地址 // const wsUri = "ws://10.27.100.151:5000/occpws/customWebSocket"; const wsUri = "wss://cloudiot.notioni.com/ws/occpws/customWebSocket"; websock = new WebSocket(wsUri,getToken()) websock.onmessage = function (e) { websocketonmessage(e) } websock.onclose = function (e) { websocketclose(e) } websock.onopen = function () { websocketOpen() heartCheck.start(); } // 連接發(fā)生錯(cuò)誤的回調(diào)方法 websock.onerror = function () { console.log('WebSocket連接發(fā)生錯(cuò)誤') isConnect = false; //連接斷開修改標(biāo)識(shí) reConnect(); //連接錯(cuò)誤 需要重連 } } // 實(shí)際調(diào)用的方法 function sendSock(agentData, callback, key) { if (!websock){ initWebSocket() } globalCallback.set(key,callback) if (websock.readyState === websock.OPEN) { // 若是ws開啟狀態(tài) websocketsend(agentData) } else if (websock.readyState === websock.CONNECTING) { // 若是 正在開啟狀態(tài),則等待1s后重新調(diào)用 setTimeout(function () { sendSock(agentData, callback,key) }, 2000) } else { // 若未開啟 ,則等待1s后重新調(diào)用 setTimeout(function () { sendSock(agentData, callback,key) }, 2000) } } function getSock(key,callback) { globalCallback.set(key,callback) } // 數(shù)據(jù)接收 function websocketonmessage(e) { let ret = JSON.parse(decodeUnicode(e.data)) if (!ret) { heartCheck.reset() } else { if (ret.msg === "websocket connect success") { } else { if (ret.method === "webSocket_device_transport"){ const callback = globalCallback.get(ret.sn) if (callback && typeof callback === "function") callback(ret); }else if(ret.method === "webSocket_device_alarm"){ const callback = globalCallback.get('deviceAlert') if (callback && typeof callback === "function") callback(ret); } } } // globalCallback(JSON.parse(e.data)) function decodeUnicode(str) { str = str.replace(/\\/g, "%"); //轉(zhuǎn)換中文 str = unescape(str); //將其他受影響的轉(zhuǎn)換回原來 str = str.replace(/%/g, "\\"); //對(duì)網(wǎng)址的鏈接進(jìn)行處理 str = str.replace(/\\/g, ""); return str; } } // 數(shù)據(jù)發(fā)送 function websocketsend(agentData) { console.log("數(shù)據(jù)發(fā)送",JSON.stringify(agentData)) websock.send(JSON.stringify(agentData)) } // 關(guān)閉 function websocketclose(e) { console.log(e) isConnect = false ; //斷開后修改標(biāo)識(shí) heartCheck.stop() console.log('connection closed (' + e.code + ')') } // 創(chuàng)建 websocket 連接 function websocketOpen(e) { isConnect = true console.log('連接成功') } //登錄成功后再建立連接 //initWebSocket() // 將方法暴露出去 export { sendSock, getSock, createWebSocket, closeWebSocket, initWebSocket } ———————————————— 版權(quán)聲明:本文為CSDN博主「Hello 阿凡達(dá)」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/qq_38746300/article/details/128675243 |
|