基于koa的前后端分離的socket.io使用

1、websocket

????????websocket是html5出的協(xié)議,它是基于TCP協(xié)議,利用http協(xié)議建立連接,實現(xiàn)了客戶端和服務(wù)器端的雙向通信,對http協(xié)議是個很好的補充。

????????具體的原理在這里不再贅述,詳情可以參照知乎用戶Ovear的回答,說的十分生動形象:WebSocket 是什么原理?為什么可以實現(xiàn)持久連接?

2、socket.io

????????socket.io實現(xiàn)了對websocket的封裝,使用戶可以專注于實現(xiàn)websocket的功能(雙向通信),而不必理會底層的實現(xiàn)。由于不同版本的瀏覽器對websocket的支持不同,為了兼容不同版本、實現(xiàn)相關(guān)功能,socket.io底層實現(xiàn)根據(jù)瀏覽器的支持分別使用了http輪詢、WebSocket還有其他的技術(shù)手段實現(xiàn)功能。

3、socket.io使用

3.1、socket.io客戶(瀏覽器)端

socket.io包括客戶端和服務(wù)端的api,創(chuàng)建客戶端socket.io有兩種方式,詳細內(nèi)容見w3c教程

????????一種是傳統(tǒng)的script標簽引入:

<script src="/socket.io/socket.io.js"></script>

<script>

? const socket = io('http://localhost');

</script>

????????一種是node環(huán)境下利用import引入模塊:

const io = require('socket.io-client');

// or with import syntax

import io from 'socket.io-client';

然后就可以初始化一個socket:io(url[, options]),例如:const socket = io('ws://127.0.0.1:3500/deviceInfo', {query: { id: 1}})

????????url (字符串):連接的服務(wù)端地址,例如本例中的:ws://127.0.0.1:3500,默認的指向widnow.location;

????????option (Object):選項,常用參數(shù)有以下幾種

? ? ? ? ? ? ? ? ---forceNew (布爾型)是否重用當前已存在的鏈接;

? ? ? ? ? ? ? ? ---path (字符串)自定義path,連接的路徑;

? ? ? ? ? ? ? ? ---query(Object)攜帶查詢選項;

? ? ? ? Return Socket

在這里要注意,socket.io有幾個概念來區(qū)分不同地址的鏈接:

????????1、url:目標地址,即服務(wù)器所在地址,

? ? ? ? 2、命名空間(namespace):一個服務(wù)器所在的地址可以有多個命名空間,例如客戶端連接的地址為ws://127.0.0.1:3500/admin,命名空間為admin;當客戶端連接的地址為ws://127.0.0.1:3500/customer,命名空間為customer,這樣客戶端根據(jù)命名空間的不同連接不同的服務(wù)端后臺命名空間。

????????????????---在不聲明新的命名空間情況下,系統(tǒng)會默認使用default namespace。

????????????????---不同命名空間下的socket是不能互相通信了,是處于隔離狀態(tài)的。

? ? ? ? 3、房間(room):一個命名空間可以有多個房間,這在實際開發(fā)中很有用,例如根據(jù)id查詢儀器信息:ws://127.0.0.1:3500/deviceInfo,可以將id為1的儀器信息連接到deviceInfo命名空間的1房間,將id為2的儀器信息連接到deviceInfo命名空間的2房間,這樣訪問兩個房間的用戶就可以準確的獲取相關(guān)的信息。

????????????????---在不加入或指定room的情況下,socket.io 會默認分配一個default room

????????????????---同一room下的socket可以廣播消息,不同room下的socket是不能互相通信了,是處于隔離狀態(tài)的。

? ? ? ? ? ? ? ?記住一點:一個socket可以有多個namespace,每個namespace可以有多個room,每個namespace和room之間是隔離的不能互相通信,room可以加入但是namespace在連接時就要指定。

客戶端通過io新建的tcp連接只有一個,例如:

const socket = io();

const adminSocket = io('/admin');

const customerSocket = io('/customer');

注意:重用相同的命名空間將會創(chuàng)建兩個連接:

const socket = io();

const socket2 = io();//兩個不同的socket,和上面的socket不同

按照上面的內(nèi)容,下例為實時獲取后臺發(fā)送的設(shè)備信息,將id作為參數(shù)發(fā)送到后臺,后臺根據(jù)id創(chuàng)建room進行前后端的雙向通信(示例1):

// 設(shè)備信息

export function GetDeviceParam(_id = 0) {

? const socket = io('ws://127.0.0.1:3500/deviceInfo', {

? ? query: { id: _id }

? })

? socket.on('deviceParam', (d) => {

? ? console.log('deviceParam:', d)

? })

? socket.on('receiveMsg', (d) => {

? ? console.log('receiveMsg:', d)

? })

3.2、socket.io服務(wù)器端

?安裝socket.io

????????????$ npm install socket.io

????????socket.io的安裝十分簡單,就像普通的npm包一樣直接安裝就好,詳細內(nèi)容見w3c教程

?????????我們使用koa框架來搭建服務(wù)器端,在koa中使用socket.io:

? ?????????????const ?app = new koa(),

? ? ? ? ?????????????????IO = require('socket.io')

????????????????????????server = require('http').createServer(app.callback());

? ? ? ? ? ? ? ??const io = IO(app);

????????這樣就新建了一個socket的io,利用io.on('setClientMsg',function(sockect){})就能監(jiān)聽客戶端setClientMsg方法發(fā)送的消息,io.emit('setServeMsg',serverData)就能想客戶端發(fā)送的消息serverData。

????????如果我們服務(wù)器端只擁有這個幾個websocket方法,這種創(chuàng)建方法十分簡潔明了,但是實際工作中,往往使用websocket接口可能只有那么幾個,其他的都是http接口,而且可能需要對于不同的用戶的客戶端需要建立不同的websocket的接口,那應(yīng)該怎么使用呢(示例1)?

一、服務(wù)器端http接口和websocket接口并存

????????工程目錄:

? ??????????????????????????const koa = require('koa'),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? koaBody = require('koa-body'),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? logger = require('koa-logger'),

? ????????????????????????????????????json = require('koa-json'),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cors = require('@koa/cors'),

? ????????????????????????????????????static = require('koa-static'),

? ????????????????????????????????????koaViews = require('koa-views'),

? ????????????????????????????????????koaNunjucks = require('koa-nunjucks-2'),

? ????????????????????????????????????path = require('path'),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? router = require('./api'),

? ????????????????????????????????????app = new koa(),

? ????????????????????????????????????server = require('http').createServer(app.callback()),

????????????????????????????????????? creatSocket = require('./socket');

????????????????????????????????//io = require('socket.io')(app);

????????????????????????????????// 使用各種中間件

????????????????????????????????app.use(logger()); //控制臺日志

????????????????????????????????app.use(koaBody({

????????????????????????????????????? multipart: true, // 支持文件上傳

????????????????????????????????????? encoding: 'gzip',

????????????????????????????????????????? formidable: {

? ????????????????????????????????????????????? uploadDir: path.join(__dirname, 'uploads')

? ????????????????????????????????????????????????}

????????????????????????????????????????}));

????????????????????????????????????app.use(json()); //響應(yīng)json化

????????????????????????????????????app.use(cors()); //設(shè)置跨域cors

????????????????????????????????????//靜態(tài)文件

????????????????????????????????????app.use(static(

????????????????????????????????????????? path.join(__dirname, './')

????????????????????????????????????????));

????????????????????????????????????//模板渲染

????????????????????????????????????// app.use(koaViews(path.join(__dirname, './views'), {

????????????????????????????????????????????????//? extension: 'ejs'

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // }));//ejs模板

????????????????????????????????????????app.use(koaNunjucks({

? ????????????????????????????????????????????ext: 'njk', //njk,html

????????????????????????????????????????????? path: path.join(__dirname, './views'),

? ????????????????????????????????????????????????nunjucksConfig: {

? ????????????????????????????????????????????????????????? trimBlocks: true

????????????????????????????????????????????????????? }

????????????????????????????????????????????????})); //Nunjucks模板

????????????????????????????????????????????????//使用路由

????????????????????????????????????????????????app.use(router.routes());

????????????????????????????????????????????????//websocket

????????????????????????????????????????????????creatSocket(server);//將新建的socket服務(wù)傳入函數(shù)

????????????????????????????????????????????????server.listen(3500);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? console.log(`-----------服務(wù)運行成功,本地端口:3500----------`);????????????????????????????

????????creatSocket.js

const IO = require('socket.io'),

? {

? ? getFulldate

? } = require('../util');

function creatSocket(app) {

? const io = IO(app);

? //每個客戶端socket連接時都會觸發(fā) connection 事件

? io.on("connection", function(clientSocket) {

? ? clientSocket.emit("receiveMsg", '連接整體的socket');

? ? console.log('連接整體的socket');

? });

? //單獨的命名空間

? //命名空間:監(jiān)聽屬性改變的,deviceInfo

? const deviceIo = io.of('/deviceInfo');

? let deviceId = '';

? deviceIo.on("connection", function(clientSocket) {

? ? //console.log('clientSocket.handshake.query.id:', clientSocket.handshake.query.id)

? ? deviceId = clientSocket.handshake.query.id;

? ? clientSocket.emit("receiveMsg", '連接deviceInfo的socket');

? ? console.log('連接deviceInfo的socket');

? ? clientSocket.join(deviceId); //加入房間

? ? //deviceInfo下的room

? ? setInterval(function() {

? ? ? let time = getFulldate();

? ? ? clientSocket.to(deviceId).emit(`deviceParam`, `deviceParam ${deviceId} time:` + time);

? ? }, 5000)

? });

}

module.exports = creatSocket?

? ? ? ?其中creatSocket是將websocket的api封裝在另外的文件夾中,具體代碼可見github

二、服務(wù)器端websocket處理動態(tài)接口

? ? ? ? 類似于http接口,將id等參數(shù)傳遞到服務(wù)器端,服務(wù)器后臺根據(jù)handshake.query.id獲取到id的取值,socket.io可以根據(jù)id等參數(shù)產(chǎn)生新的房間,然后與客戶端連接。

詳情看上面的creatSocket.js,利用jion加入房間,然后利用to(roomid).emit發(fā)送消息

clientSocket.join(deviceId); //加入房間

clientSocket.to(deviceId).emit(`deviceParam`, `deviceParam ${deviceId} time:` + time);

3.3、運行結(jié)果


deviceInfo返回結(jié)果

點擊websocket按鈕,會調(diào)用GetDeviceParam,連接到后臺ws://127.0.0.1:3500/deviceInfo,實現(xiàn)數(shù)據(jù)的實時刷新,由此完成了前后端基于websocket的雙向?qū)崟r通信。服務(wù)器端代碼示例見github


? ??????

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容