nodejs 中使用websocket 并且区分空间,实现收到客服端消息 同步给房间内所有连接,小程序中使用websocket,区分房间、空间

news2024/11/23 19:35:20

❤️砥砺前行,不负余光,永远在路上❤️

目录

    • 前言
    • 一、服务端
      • 1、主要是通过node+express+websocket搭建
      • 2、代码大概结构
      • 3、nodejs 启动入口文件 引入自己的websocket文件,这里是为了和 http 服务结合起来,将server传入
      • 4、websocket.js 工具文件完整代码
    • 二、客户端部分
      • 1、小程序在oonload中 连接websocket
      • 2、this.connectWebSocket 代码和向服务器发送消息的代码如下
      • 3、客户端说明

前言

因为业务需要我这里是小程序 结合 nodejs来实现的websocket通信

一、服务端

1、主要是通过node+express+websocket搭建

2、代码大概结构

在这里插入图片描述

3、nodejs 启动入口文件 引入自己的websocket文件,这里是为了和 http 服务结合起来,将server传入

自己的文件中,http.createServer(app)
客户端那边就可以通过以下网址去访问了

ws://${IP}:3000/    // 3000也就是自己node启动的端口 另外 + 本机ip
const { port } = require('../config/index')
var app = require('../app');
var debug = require('debug')('projectname:server');
var http = require('http');
app.set('port', port);
var server = http.createServer(app);

try {
	const Websocket = require('../utils/websocket')(server)
} catch (error) {
	console.log(error);
}

4、websocket.js 工具文件完整代码

做个说明,这个是包含了我自己的部分业务需要,如果自己用不上数据库相关的东西可以删除,
我是在 headres 拿到token自己去去解析用户信息,还在headers 中携带了一个用户信息,
区分空间主要是在 连接websocket 的url上处理的,相同的房间请求同一个url,代码中也包含了详细注释。

/*
 * @Date: 2023-05-24 09:23:47
 * @LastEditTime: 2023-05-26 13:51:52
 */

const jwt = require('../utils/jwt')
const { literal, Op, Sequelize } = require("sequelize");
const { User, Room, RoomUser, Record, Standings } = require('../models/index')
const { uuid } = require('../utils/index');


const WebSocket = require('ws');
module.exports = function (server) {
	try {
		// 创建WebSocket服务器
		const wss = new WebSocket.Server({ server });
		// 管理连接和空间的数据结构
		const connections = new Map(); // 存储连接的Map,key为空间名称,value为连接数组

		// 监听连接事件
		wss.on('connection', async function connection (ws, req) {
			addToSpace(ws, req.url.split('/')[1]);
			console.log('New client connected', '------------------------------>');
			// 在这里可以访问 WebSocket 请求的请求头
			let { id } = jwt.verifyToken(req.headers.token);
			const roomId = req.headers['room-id']


			//通过 id去获取用户信息  / 不然可能用户更新了信息但是还是原来的token
			const res = await User.findOne({
				where: { id },
				raw: true,
			});
			//查看用户是否加入到房间
			let userIsJoin = await Record.findOne({
				where: { roomId, fromId: id, type: '2' },
				raw: true,
			});
			if (!userIsJoin) { //没有就加一个
				await Record.create({ id: uuid(), roomId, fromId: id, type: '2', createTime: Sequelize.fn('NOW') })
			}
			//有新的连接 也要下发信息
			console.log(`${res.nickName}加入了房间`, roomId);
			const data = { spaceName: req.url.split('/')[1], userJoin: `${res.nickName}加入了房间` }
			handleIncomingMessage(ws, JSON.stringify(data));

			// 监听消息接收事件
			ws.on('message', function incoming (message) {
				console.log('Received message:', message.toString('utf8'), '----------->', message);
				handleIncomingMessage(ws, message.toString('utf8'));
			});

			// 监听连接关闭事件
			ws.on('close', async function close () {
				console.log('Client disconnected');
				//查看用户是否加入到房间
				let userIsJoin = await Record.findOne({
					where: { roomId, fromId: id, type: '3' },
					raw: true,
				});
				if (!userIsJoin) { //没有就加一个
					await Record.create({ id: uuid(), roomId, fromId: id, type: '3', createTime: Sequelize.fn('NOW') })
				}

				const data = { spaceName: req.url.split('/')[1], userLeave: `${res.nickName}退出了房间` }
				handleIncomingMessage(ws, JSON.stringify(data));

				removeConnectionFromAllSpaces(ws);
			});
		});

		// 处理接收到的消息
		function handleIncomingMessage (ws, message) {
			const messageString = JSON.parse(message);
			// 假设消息格式为 "SPACE_NAME|MESSAGE_CONTENT"
			const { spaceName } = messageString
			// connection.send(content);
			const connectionsInSpace = connections.get(spaceName);
			if (connectionsInSpace) {
				// 向当前空间的所有连接发送消息
				connectionsInSpace.forEach(connection => {
					// if (connection == ws && connection.readyState === WebSocket.OPEN) {
					// 	console.log('send----------------------------');
					connection.send(JSON.stringify(messageString));
					// }
				});
			}
		}

		// 将连接添加到指定空间
		function addToSpace (ws, spaceName) {
			console.log(spaceName, '房间名称');
			let connectionsInSpace = connections.get(spaceName);
			if (!connectionsInSpace) {
				connectionsInSpace = new Set();
				connections.set(spaceName, connectionsInSpace);
			}
			connectionsInSpace.add(ws);
		}

		// 将连接从所有空间中移除
		function removeConnectionFromAllSpaces (ws) {
			connections.forEach(connectionsInSpace => {
				connectionsInSpace.delete(ws);
			});
		}
	} catch (error) {
		console.log(error);
	}
}

二、客户端部分

1、小程序在oonload中 连接websocket

onLoad(options) {
        console.log(options);
        const { roomNum, roomId } = options
        if (roomNum) {
                this.setData({ roomNum })
        }
        // this.getRoomInfo(wx.getStorageSync('roomId')) 这是拉取房间信息的接口  相当yu是在收到服务端消息之后自己去获取最新的信息
        this.connectWebSocket()  // 连接WebSocket服务器
},

2、this.connectWebSocket 代码和向服务器发送消息的代码如下

下面的WS_URL有标注。roomNum 就是房间编号是需要客户端这边确定 。
// WS_URL = ws://${IP}:3000/

/**
 * 连接WebSocket服务器
 */
connectWebSocket: function () {
        var _this = this
        wx.connectSocket({
                url: WS_URL + wx.getStorageSync('roomNum'),
                header: {
                        "token": wx.getStorageSync('token'),
                        "room-id": wx.getStorageSync('roomId'),
                },
                success: (res) => {
                        console.log('WebSocket连接成功:', res)
                        // wechatSIPlayAudio('WebSocket连接成功')
                        // _this.getRoomInfo(wx.getStorageSync('roomId'))
                },
                fail: function (res) {
                        console.log('WebSocket连接失败:', res)
                }
        })
        wx.onSocketOpen(function (res) {
                console.log('WebSocket连接已打开')
                _this.setData({ socketOpen: true })
        })
        wx.onSocketError(function (res) {
                console.log('WebSocket连接打开失败:', res)
        })
        wx.onSocketClose(function (res) {
                console.log('WebSocket连接已关闭:', res)
                _this.setData({
                        socketOpen: false
                })
        })
        wx.onSocketMessage(function (res) {
                //  收到服务器发送的数据之后  重新拉取 更新列表  
                const data = JSON.parse(res.data)
                console.log('接收到服务器发送的数据:', data)
                if (data.toId === wx.getStorageSync('userId')) {
                        getApp().util.toast(data.toMsg)
                        wechatSIPlayAudio(data.toMsg)
                        setTimeout(() => {
                                _this.getRoomInfo(wx.getStorageSync('roomId'))
                        }, 3000);
                } else {
                        _this.getRoomInfo(wx.getStorageSync('roomId'))
                }
                //用户加入 播报
                if (data.userJoin) {
                        wechatSIPlayAudio(data.userJoin)
                }

                //用户退出 播报
                if (data.userLeave) {
                        wechatSIPlayAudio(data.userLeave)
                }
        })
},
/**
 * 发送WebSocket消息
 */
sendSocketMessage: function (params) {
        if (this.data.socketOpen) {
                wx.sendSocketMessage({
                        data: JSON.stringify({
                                spaceName: wx.getStorageSync('roomNum'),
                                ...params
                        })
                })
        }
},

3、客户端说明

小程序这边逻辑 主要是在 收到服务端发送的消息之后去拉取最新的数据,来更新界面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/589378.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【课代表笔记】直播回顾:Top药企的数字化实践集锦

【K讲了】系列直播之医药行业第一期:Top药企的数字化实践集锦前不久已在视频号和大家如期见面,以下是课代表为大家抄好的笔记~~ 斯歌K2的医药行业经验 K2在医药领域拥有丰富的客户积累及实施经验,全球TOP 10药企中有7家选择K2。斯歌K2已在医药…

JAVA POI excel 添加下拉字典的方式与案例 以及图文详解及个人理解

场景 原有的Excel 某一个 sheet 页中某些列需要添加指定的字典下拉,而这些字典的值又是确认的。 有两种思路: 一、如果给定的下拉字典值是确定的而且关联原有列的位置也不会变,那么这些数据可以固定写死在代码中,也是最简单的一…

身份集权设施保护之Kerberos协议

一、Kerberos协议介绍 Kerberos是一种由MIT(麻省理工大学)提出的一种网络身份验证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证。该认证过程的实现不依赖于主机操作系统的认证,无需基于主机地址的信任&#xff0…

Live800:新的消费趋势下,企业在线客服需哪些改变?

从2021到2023,新模式、新业态、新产业层出不穷,新兴习惯也不断涌现,我们见证了消费品牌的“新物种爆炸”,见证了各行业的线上迁移。 这一切催化消费市场持续更新,消费趋势演变的路径也发生了变化,从以前的…

“数字”厨电成新宠?“小米卷出光学拍摄“天花板”?|3C数码行业SMI社媒心智品牌榜

手机行业SMI社媒心智品牌榜核心解读 智能手机“乍暖还寒”,龙头品牌仍稳占消费者心智 比拼屏幕、赶超系统、迭代形态、拓展概念?眼花缭乱过后,产品精益求精,建立稳固的消费者认知,才是“保鲜”关键。在最新发布的数说…

趣味LFS实验部署

LFS文件准备 LFS项目官方网站:https://www.linuxfromscratch.org/ 查找宿主系统必须安装的软件包 https://www.linuxfromscratch.org/lfs/downloads/stable/LFS-BOOK-11.1-NOCHUNKS.html 安装依赖: #先来看看我此处的Yum仓库环境: CentOS-…

ArcGis系列-java调用GP分析

1,实现流程 创建GPServer,使用ArcgisPro添加GP工具运行,然后使用共享web服务发布运行成功的GP任务根据发布成功的GPServer发布地址,解析出GP服务的输入参数和输出参数前端输入gp服务需要的参数,发送给后端来异步提交后端提交后创建轮询任务等待执行结果…

3D知识入门

3D场景必备&#xff1a;scene, renderer, light, camera, model 一个基本代码: <script src"https://cdn.bootcdn.net/ajax/libs/three.js/r127/three.min.js"></script>var scene new THREE.Scene();var camera new THREE.PerspectiveCamera(75,windo…

【EKS】基于Amazon EKS搭建kubernetes集群

文章目录 前言 | 亚马逊云科技 re:Invent前沿资讯一、介绍篇&#x1f3a8;什么是AWS 云计算什么是Amazon EKS 二、部署篇&#x1f528;1、创建集群VPC2、创建集群子网3、创建IGW网关4、创建路由表与子网绑定5、EKS集群创建6、创建kubeconfig配置文件7、添加计算节点组8、查看EK…

IC卡水表大多都用在什么项目上?有什么功能特点吗?

IC卡水表是一种先进的计量仪表&#xff0c;广泛应用于许多项目&#xff0c;其功能特点使其在许多领域得到广泛应用。 首先&#xff0c;IC卡水表可以应用于自来水的计量&#xff0c;它可以高精度地测量水的流量&#xff0c;提供给用户准确的用水量信息&#xff0c;从而有助于用户…

分片架构,Redis Cluster 分析

分片架构解决的问题 通过堆机器&#xff0c;提升读写性能&#xff0c;与存储性能 分片架构设计要点 分片规则 选择Cardinality大的作为分片键&#xff0c;尽可能保证数据分布均匀 常见分片键&#xff1a; 基于主键&#xff08;业务型数据&#xff09;&#xff0c;基于时间…

JavaScript高级四、高阶技巧

零、文章目录 JavaScript高级四、高阶技巧 1、深浅拷贝 首先浅拷贝和深拷贝只针对引用类型 &#xff08;1&#xff09;浅拷贝 浅拷贝&#xff1a;拷贝对象的属性的值&#xff08;简单类型存的值就是值本身&#xff0c;引用类型存的值是对象的堆地址&#xff09;&#xff0c…

windows里怎么杀死一个进程?

我们可以使用 taskkill 命令&#xff0c;可以使用该工具按照进程 ID (PID) 或映像名称终止任务。 显示帮助消息&#xff1a; taskkill /?参数列表&#xff1a; /S&#xff1a;system&#xff1a;指定要连接的远程系统。/U&#xff1a;[domain\]user&#xff1a;指定应该在哪…

【ESP-01S / ESP8266 AT指令连接阿里云物联网平台】

ESP-01S / ESP8266 AT指令连接阿里云物联网平台 阿里云物联网平台新建设备获取AT参数 AT指令介绍连接阿里云AT指令介绍MQTT固件固件下载硬件连接固件烧录 串口助手调试硬件连接测试指令 AT_Command移植总结问题排查 源码获取 关注星标公众号&#xff0c;不错过精彩内容 作者 | …

【简单实用框架】【十大排序算法直接调用】【可移植】

☀️博客主页&#xff1a;CSDN博客主页&#x1f4a8;本文由 萌萌的小木屋 原创&#xff0c;首发于 CSDN&#x1f4a2;&#x1f525;学习专栏推荐&#xff1a;面试汇总❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&#…

JeecgBoot企业级开发中实现自定义导出EXCEL的前端表格字段功能

文章目录 如何在后端实现导出前端列表字段到Excel功能需求前端的实现1. 提供一个导出的点击函数2.引入组件中的userMethod3.tableProps中导出中添加对应的查询参数4. 编写导出函数 后端逻辑的实现1.Controller层2.创建Modal类3.Sevice层 检验成果总结 如何在后端实现导出前端列…

【Android Gradle 插件】更新依赖方式,同时解决github三方库引用无法使用问题

首先看一下完整的 settings.gradle 依赖介绍 /* pluginManagement 脚本块,用于配置Gradle插件的Maven仓库,配置的是构建过程中,使用的仓库 ; pluginManagement 脚本块中的 repositories 配置 , 对应之前的 buildscript 中的 repositories 配置 ; */ pluginManagement {reposit…

技术分享 | 一文了解 MySQL Optimizer Trace 的神奇功效

作者&#xff1a;Mutlis CSDN & 阿里云 & 知乎 等平台优质作者&#xff0c;擅长Oracle & MySQL等主流数据库系统的维护和管理等 本文来源&#xff1a;原创投稿 前言 对于 MySQL 5.6 以及之前的版本来说&#xff0c;查询优化器就像是一个黑盒子一样&#xff0c;…

迪赛智慧数——柱状图(基本柱状图):购买雪糕考虑的因素

效果图 冰淇淋季节来袭&#xff0c;因其细腻凉爽的口感和浓郁的口味被广大消费者所钟爱&#xff0c;近年来已经从一款传统的解暑冷冻饮品转变为一种原料丰富、口味多元、追求健康、愉悦和高品质生活方式的休闲食品。据数据显示&#xff0c;82.2&#xff05;女性、82.3%男性消费…

chatgpt赋能python:Python中乘方的介绍

Python中乘方的介绍 在Python中&#xff0c;乘方运算指数运算&#xff0c;常用符号为“”&#xff08;例如2的3次方为23&#xff09;。使用乘方运算可以快速地进行数值计算&#xff0c;尤其是在科学和工程领域中。 为什么要使用乘方运算&#xff1f; 乘方运算主要用于处理大…