摘要:為了體現(xiàn)前后端分離,提高開發(fā)效率的精髓。轉(zhuǎn)發(fā)消息服務(wù)器將收到的來(lái)自于發(fā)送方消息中的值作為要轉(zhuǎn)發(fā)的目標(biāo)接收方,在服務(wù)器自身維護(hù)的對(duì)象中找到接收方的這個(gè)連接,然后將發(fā)送方的標(biāo)識(shí)作為轉(zhuǎn)發(fā)給接收方。
背景
簡(jiǎn)單的描述一下需求場(chǎng)景:應(yīng)用需要進(jìn)行客戶端到客戶端的通信,websocket 就能很好的進(jìn)行這一操作,目前 網(wǎng)易云信的 IM 等功能也是利用 websocket 進(jìn)行的。
必要性對(duì)前端開發(fā)人員來(lái)說(shuō),目前能夠提供 mock 服務(wù)的第三方工具還是比較多的,基本上,與后臺(tái)開發(fā)人員約定好請(qǐng)求路徑、請(qǐng)求字段和響應(yīng)字段之后就能前后臺(tái)獨(dú)立開發(fā)了。
但 websocket 服務(wù)器與 http 服務(wù)器最大的區(qū)別就是 websocket 服務(wù)器必須得一直提供服務(wù),否則客戶端之間就無(wú)法進(jìn)行通信。
為了體現(xiàn)前后端分離,提高開發(fā)效率的精髓??隙ㄊ遣荒芟劝堰壿嬋棵懞昧酥笤倥c后臺(tái)聯(lián)調(diào)的,便決定與后臺(tái)約定好了接口名和數(shù)據(jù)形式之后,用 nodejs 寫一個(gè)簡(jiǎn)單的 websocket 通信服務(wù)。
功能websocket 用的比較多的應(yīng)該就是傳輸文本(簡(jiǎn)單點(diǎn)說(shuō)就是字符串),所以,這個(gè)字符串?dāng)y帶著接收方的用戶標(biāo)識(shí)(toId),其他的信息(比如消息類型 type 和 消息內(nèi)容 data 等),通常的做法是 JSON.stringfy() 之后轉(zhuǎn)成字符串,服務(wù)端將發(fā)送方的用戶標(biāo)識(shí)(fromId)、其他的信息(比如發(fā)送過(guò)來(lái)的消息類型 type 和 消息內(nèi)容 data)等信息轉(zhuǎn)發(fā)給接收方,接收方接收到字符串之后再 JSON.parse() 進(jìn)行下一步操作。
用下面這張圖描述一下我希望 websocket 服務(wù)器能提供給客戶端的功能。
客戶端websocket 提供的方法中常用的有:new、onopen、onclose、onerror、onmessage、send
毫無(wú)疑問(wèn),對(duì)前端開發(fā)人員來(lái)說(shuō)需要與業(yè)務(wù)邏輯相結(jié)合、重點(diǎn)關(guān)注的方法就是以下三個(gè):
new在連接 websocket 服務(wù)器時(shí)傳遞自己的 id
const ws = new WebSocket(`ws://localhost:8080/websocketServer/${id}`);send
發(fā)送消息(字符串)給服務(wù)器,服務(wù)器再轉(zhuǎn)發(fā)給目標(biāo)
const msgObj = { toId: 666, type: "hello", data: "message......" }; const msgStr = JSON.stringfy(msgObj); ws.send(msgStr);sendTo
由于 send 不能接收對(duì)象或者數(shù)組類型的數(shù)據(jù),每次都得寫一個(gè)臨時(shí)的對(duì)象,再調(diào)用 JSON.stringfy() 方法轉(zhuǎn)成字符串。
那就封裝一個(gè)符合自己業(yè)務(wù)場(chǎng)景的方法,簡(jiǎn)化開發(fā)過(guò)程中實(shí)現(xiàn)發(fā)送功能的步驟。
// 在原型對(duì)象上增加的 sendTo 方法 if ( ! WebSocket.prototype.sendTo ) { WebSocket.prototype.sendTo = function(toId, type, data) { const msg = JSON.stringify({ toId, type, data }); this.send(msg); } } // 調(diào)用形式 ws.sendTo(toId, type, data);onmessage
接收消息,通常會(huì)根據(jù)消息中 type 的不同值:
自己做點(diǎn)什么
給對(duì)方一個(gè)響應(yīng)(調(diào)用 sendTo 方法)
ws.onmessage = function(msg) { const { fromId, type, data } = JSON.parse(msg); switch (type) { case "message": // 自己做點(diǎn)什么 break; case "hello": // 返回一個(gè)消息 this.sendTo(fromId, "response", "response data"); } }
最后簡(jiǎn)單的封裝一下初始化方法,方便在不同的地方使用。
const wsUrl = "ws://localhost:8080/"; const wsPath = "socketServer/"; // 生成實(shí)例 let ws = null; const connect = async () => { ws = await connectWS(uid, messageHandle); // ... // ... // 連接成功之后做點(diǎn)什么 } function connectWS(uid, msgHandle) { if ( ! WebSocket.prototype.sendTo ) { WebSocket.prototype.sendTo = function(toId, type, data) { const msg = JSON.stringify({ toId, type, data }); this.send(msg); } } return new Promise((resolve, reject) => { const ws = new WebSocket(`${wsUrl}${wsPath}${uid}`); ws.onopen = () => { console.log("open"); resolve(ws); }; ws.onclose = () => { console.log("close"); }; ws.onerror = () => { console.log("error"); }; ws.onmessage = (msg) => { msgHandle(JSON.parse(msg.data)); }; }); } // 接收消息的處理函數(shù) function messageHandle(msgData) { const { fromId, type, data } = msgData; switch (type) { case "": break; default: break; } }服務(wù)端
常用的 nodejs-websocket 和 express-ws 兩個(gè)框架,都需要自身維護(hù)一個(gè)對(duì)象來(lái)記錄每個(gè) websocket 連接是哪個(gè)用戶,從而實(shí)現(xiàn)消息轉(zhuǎn)發(fā)。
以 express-ws 為例:
記錄連接首先,服務(wù)端要記錄當(dāng)前連接的用戶,以便轉(zhuǎn)發(fā)消息的時(shí)候能夠正確發(fā)送給目標(biāo)。
轉(zhuǎn)發(fā)消息服務(wù)器將收到的來(lái)自于發(fā)送方消息中的 toId 值作為要轉(zhuǎn)發(fā)的目標(biāo)(接收方),在服務(wù)器自身維護(hù)的對(duì)象中找到接收方的這個(gè)連接,然后將發(fā)送方的標(biāo)識(shí)作為 fromId 轉(zhuǎn)發(fā)給接收方。
const express = require("express"); const express_ws = require("express-ws"); const app = express(); const wsObj = {}; express_ws(app); app.ws("/socketServer/:uid", (ws, req) => { const uid = req.params.uid; wsObj[uid] = ws; ws.onmessage = (msg) => { let { toId, type, data} = JSON.parse(msg.data); const fromId = uid; if (fromId != toId && wsObj[toId]) { // wsObj[toId] 表示 接收方 與服務(wù)器的那條連接 // wsObj[fromId] 表示 發(fā)送方 與服務(wù)器的那條連接 wsObj[toId].send(JSON.stringify( { fromId, type, data } )) } } }); app.listen(8080);
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/106307.html
摘要:在中,在不同的域名下面進(jìn)行數(shù)據(jù)交互,就會(huì)遇到跨域問(wèn)題,說(shuō)到跨域首先要從同源說(shuō)起,瀏覽器為了提供一種安全的運(yùn)行環(huán)境,各個(gè)瀏覽器廠商協(xié)定使用同源策略。在上面說(shuō)過(guò)是不受同源策略限制的,但是出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源請(qǐng)求。 對(duì)于前端開發(fā)來(lái)說(shuō)跨域應(yīng)該是最不陌生的問(wèn)題了,無(wú)論是開發(fā)過(guò)程中還是在面試過(guò)程中都是一個(gè)經(jīng)常遇到的一個(gè)問(wèn)題,在開發(fā)過(guò)程中遇到這個(gè)問(wèn)題的話一般都是找后端同學(xué)去解決...
摘要:流控制通常就是在客戶端的頁(yè)面使用一個(gè)隱藏的窗口向服務(wù)端發(fā)出一個(gè)長(zhǎng)連接的請(qǐng)求。和長(zhǎng)鏈接以上幾種服務(wù)器推的技術(shù)中長(zhǎng)輪詢和流控制其實(shí)都是基于長(zhǎng)鏈接來(lái)實(shí)現(xiàn)的,也就是中所謂的。通信協(xié)議于年被定為標(biāo)準(zhǔn),并被所補(bǔ)充規(guī)范。 初探WebSocket node websocket socket.io 我們平常開發(fā)的大部分web頁(yè)面都是主動(dòng)‘拉’的形式,如果需要更新頁(yè)面內(nèi)容,則需要刷新一個(gè),但Slack工...
摘要:好的,這樣以來(lái)我們的前期準(zhǔn)備工作就已經(jīng)完成了,下面我們來(lái)搭建聊天室對(duì)應(yīng)的客戶端和服務(wù)器端。 websocket簡(jiǎn)介 websocket其實(shí)HTML中新增加的內(nèi)容,其本質(zhì)還是一種網(wǎng)絡(luò)通信協(xié)議,以下是websocket的一些特點(diǎn): (1)因?yàn)檫B接在端口80(ws)或者443(wss)上創(chuàng)建,與HTTP使用的端口相同,幾乎所有的防火墻都不會(huì)阻塞WebSocket鏈接 (2)因...
摘要:好的,這樣以來(lái)我們的前期準(zhǔn)備工作就已經(jīng)完成了,下面我們來(lái)搭建聊天室對(duì)應(yīng)的客戶端和服務(wù)器端。 websocket簡(jiǎn)介 websocket其實(shí)HTML中新增加的內(nèi)容,其本質(zhì)還是一種網(wǎng)絡(luò)通信協(xié)議,以下是websocket的一些特點(diǎn): (1)因?yàn)檫B接在端口80(ws)或者443(wss)上創(chuàng)建,與HTTP使用的端口相同,幾乎所有的防火墻都不會(huì)阻塞WebSocket鏈接 (2)因...
閱讀 1488·2021-09-10 11:27
閱讀 2444·2019-08-30 15:53
閱讀 1388·2019-08-30 13:10
閱讀 3011·2019-08-30 11:09
閱讀 1121·2019-08-29 17:23
閱讀 692·2019-08-29 17:05
閱讀 2972·2019-08-29 15:10
閱讀 2374·2019-08-29 13:22