【node进阶】深入浅出websocket即时通讯(二)-实现简易的群聊私聊

news2025/7/13 20:05:37

✅ 作者简介:一名普通本科大三的学生,致力于提高前端开发能力
✨ 个人主页:前端小白在前进的主页
🔥 系列专栏 : node.js学习专栏
⭐️ 个人社区 : 个人交流社区
🍀 学习格言: ☀️ 打不倒你的会使你更强!☀️
💯 刷题网站:这段时间有许多的小伙伴在问有没有什么好的刷题网站,博主在这里给大家推荐一款刷题网站:👉点击访问牛客网👈牛客网支持多种编程语言的学习,各大互联网大厂面试真题,从基础到拔高,快来体验一下吧!


在这里插入图片描述


🔥前言

相信在上一篇的文章中大家对websocket的基本轮廓包括基础知识做了一定的了解学习,那么本篇文章将会从demo的角度去实现群聊私聊的功能,一起来看看吧!

📃目录

  • 实现效果
  • 前台核心代码
    • 设置不同状态
    • 封装一个发送信息函数
  • 后台核心代码
    • 获取到token
    • 封装一个给前端返回消息的函数
    • 定义与前端一致的状态对象
    • 封装一个js文件处理token
    • 校验token
  • 前台实现(所有代码)
  • node后台实现(所有代码)
  • 小结

实现效果

在这里插入图片描述
这里有个小遗憾,我忘给私聊添加一个简单的样式了,这也是最后我突然发现了,xdm,你们在下面可以去添加一下私聊的简单dom,我这里就用控制台打印实现了!


前台核心代码


设置不同状态

在这里设置了四种状态,每种状态对应着相应的功能,有获取群列表信息转到群聊转到私聊

const WebSocketType = {
            Error: 0, //错误
            GroupList: 1,//群列表
            GroupChat: 2,//群聊
            SingleChat: 3//私聊
        }

封装一个发送信息函数

因为我们给后端发送信息时只能传字符串,所以我们将传给后端的这个对象转换成字符串的形式,使用内置方法JSON.stringify()

function createMessage(type, data, to) {
    return JSON.stringify({
        type,
        data,
        to
    });
}

后台核心代码

获取到token

使用js中的内置方法new URL()获取到请求地址的参数,注意,这里的req.url是请求地址后面的参数!

const myURL = new URL(req.url, "http://127.0.0.1:3000")
const token = myURL.searchParams.get("token")

封装一个给前端返回消息的函数

与前台的代码相似,给前台对应的状态返回信息!

function createMessage(type, user, data) {
    return JSON.stringify({
        type: type,
        user: user,
        data: data
    });
}

定义与前端一致的状态对象

实现与前台对象相对应的状态!

const WebSocketType = {
    Error: 0, //错误
    GroupList: 1, //群列表
    GroupChat: 2, //群聊
    SingleChat: 3 //私聊
}

封装一个js文件处理token

const jwt = require('jsonwebtoken')
const secret = 'ccc-data'
const JWT = {
	//jwt的sign()生成token
    generate(value,expires) {
        return jwt.sign(value,secret,{expiresIn:expires})
    },
    //解密token,验证
    verify(token) {
        try {
            return jwt.verify(token,secret)
        } catch (error) {
            return false
        }
    }
}

module.exports = JWT

校验token

这里校验token,成功后,就会给前台返回欢迎来到本聊天室的字样,并且给我们的句柄添加一个user属性,目的是让我们明确是谁进入到了聊天室,返回进入聊天室这个人的信息!

    // 校验token
    const payload = JWT.verify(token)
    if (payload) {
        ws.send(createMessage(WebSocketType.GroupChat, null, '欢迎来到本聊天室'));
        ws.user = payload
        //群发
        sendAll()
    } else {
        ws.send(createMessage(WebSocketType.Error, null, 'token过期'))
    }

前台实现(所有代码)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./stylesheets/chat.css">
</head>

<body>
    <h1 id="h1">欢迎来到聊天室</h1>
    <h3 id="h3"></h3>
    <input type="text" id="text"><button id="send">send</button>
    <select id="select"></select>
    <div id="chat"></div>
    <!-- 建立socket连接,带着token,后端验证 -->
    <script>
        h1.innerHTML = `${localStorage.getItem("username")}欢迎您来到聊天室`
        const WebSocketType = {
            Error: 0, //错误
            GroupList: 1,//群列表
            GroupChat: 2,//群聊
            SingleChat: 3//私聊
        }
        function createMessage(type, data, to) {
            return JSON.stringify({
                type,
                data,
                to
            });
        }
        const ws = new WebSocket(`ws://localhost:8080?token=${localStorage.getItem("token")}`)
        ws.onopen = () => {
            console.log('连接成功!');
        }
        ws.onmessage = (msgObj) => {
            // console.log(msgObj.data);
            msgObj = JSON.parse(msgObj.data)
            switch (msgObj.type) {
                case WebSocketType.Error:
                    localStorage.removeItem("token")
                    location.href = '/login'
                    break;

                case WebSocketType.GroupList:
                    console.log(JSON.parse(msgObj.data));
                    const onlineList = JSON.parse(msgObj.data)
                    h3.innerHTML = ``
                    h3.innerHTML = `当前在线人数:${onlineList.length}`
                    select.innerHTML = ``
                    select.innerHTML = `<option value="all">All</option>` + onlineList.map(item => `
                        <option value="${item.username}">${item.username}</option>
                    `).join('')
                    break;
                case WebSocketType.GroupChat:
                    console.log((msgObj.user ? msgObj.user.username : '广播') + ':' + msgObj.data);
                    var para = document.createElement("p");
                    var node = document.createTextNode((msgObj.user ? msgObj.user.username : '广播') + ':' + msgObj.data);
                    para.appendChild(node);
                    chat.appendChild(para);
                    break;
                case WebSocketType.SingleChat:
                    console.log(msgObj.user.username + ':' + msgObj.data);
                    break;
            }
        }
        send.onclick = () => {
            if (select.value === 'all') {
                // console.log('群发');
                ws.send(createMessage(WebSocketType.GroupChat, text.value))
            } else {
                // console.log('私聊');
                ws.send(createMessage(WebSocketType.SingleChat, text.value, select.value))
            }
        }
    </script>
</body>

</html>

node后台实现(所有代码)

//websocket响应
const { json } = require('express');
const WebSocket = require('ws');
const JWT = require('../util/jwt');
const WebSocketServer = WebSocket.WebSocketServer
const wss = new WebSocketServer({
    port: 8080
});

wss.on('connection', function connection(ws, req) {
    const myURL = new URL(req.url, "http://127.0.0.1:3000")
    console.log(req.url);
    //获取到token,随后进行验证
    const token = myURL.searchParams.get("token")
    // 校验token
    const payload = JWT.verify(token)
    console.log(payload);
    if (payload) {
        ws.send(createMessage(WebSocketType.GroupChat, null, '欢迎来到本聊天室'));
        ws.user = payload
        //群发
        sendAll()
    } else {
        ws.send(createMessage(WebSocketType.Error, null, 'token过期'))
    }
    ws.on('message', function message(data) {
        console.log('received: %s', data);
        const msgObj = JSON.parse(data)  //解析前台传来的数据
        switch (msgObj.type) {  //通过switch分支来进行不同状态的相应操作
            case WebSocketType.GroupList: //获取进入聊天室的人员列表
                ws.send(createMessage(WebSocketType.GroupList, null, JSON.stringify(Array.from(wss.clients).map(item => item.user))))
                //由于返回的类型是set集合,所以我们通过Array.from()转化为真正的数组!
                break;
            case WebSocketType.GroupChat: //群聊分支
                //转发给其他人
                wss.clients.forEach(function each(client) {
                    if (client.readyState === WebSocket.OPEN) {
                        client.send(createMessage(WebSocketType.GroupChat, ws.user,msgObj.data),{binary : false})
                    }
                });
                break;
            case WebSocketType.SingleChat: //私聊分支
                wss.clients.forEach(function each(client) {
                    // console.log(client.user);
                    console.log(ws.user);
                    if (client.user.username === msgObj.to && client.readyState === WebSocket.OPEN) {
                        client.send(createMessage(WebSocketType.SingleChat, ws.user,msgObj.data),{binary : false})
                    }
                });
                break;
        }
    });
    ws.on('close', () => {
        wss.clients.delete(ws.user)
        sendAll()
    })
});

const WebSocketType = {
    Error: 0, //错误
    GroupList: 1, //群列表
    GroupChat: 2, //群聊
    SingleChat: 3 //私聊
}

function createMessage(type, user, data) {
    return JSON.stringify({
        type: type,
        user: user,
        data: data
    });
}

const sendAll = () => {
    //转发给其他人
    wss.clients.forEach(function each(client) {
        if (client.readyState === WebSocket.OPEN) {
            client.send(createMessage(WebSocketType.GroupList, null, JSON.stringify(Array.from(wss.clients).map(item => item.user))))
        }
    });
}

小结

这些代码看起来觉得好多好多呀,其实我们滤清思路分析一下,可以发现前后端是对应着的,按着对应关系一一去写代码就会非常轻松,这里的代码逻辑相对来说还是很清晰的,本篇文章到这里就结束了!下周开始不定时要进行js的重生之路了,将会结合许许多多的demo带领大家去学习js,不至于到头来啥也不会,少年,继续加油吧!

注意登录功能的设置token,以及axios的拦截器将会在以后的node项目实战中与大家见面,我们之前学了jwt,相信大家会写一个登录的接口和简单页面!


👑书写不易,希望大家能够给予三连支持,期待我更好的文章哟👑


在这里插入图片描述

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

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

相关文章

保姆级教程:Ant Design Vue中 a-table 嵌套子表格

前端为Ant Design Vue 版本为1.6.2&#xff0c;使用的是vue2 Ant Design Vue中 a-table 嵌套子表格&#xff0c;说的可能稍微墨迹了点&#xff0c;不过重点内容都说的比较详细&#xff0c;利于新人理解&#xff0c;高手可以自取完整代码 内容概述&#xff1a;完成样式及完整代…

在收到消息后秒级使网站变灰,不改代码不上线,如何实现?

注意&#xff1a;文本不是讲如何将网站置灰的那个技术点&#xff0c;那个技术点之前汶川地震的时候说过。 本文不讲如何实现技术&#xff0c;而是讲如何在第一时间知道消息后&#xff0c;更快速的实现这个置灰需求的上线。 实现需求不是乐趣&#xff0c;指挥别人去实现需求才…

[Vue warn]: Error in render: “TypeError: Cannot read properties of undefined(reading“category1Name“

明明页面正常显示&#xff0c;但是控制台却一直报 如下 错误 [Vue warn]:渲染错误:"TypeError:无法读取未定义的属性(读取category1Name)" 中发现的 Detail 的 vuex 仓库 import { reqDetail } from "/api" export default{actions:{async getDetail({co…

【前端修炼场】 — 这些标签你学会了么?快速拿下 “hr”

此文为【前端修炼场】第四篇&#xff0c;上一篇文章链接&#xff1a;上一篇 文章目录前言一、 常用标识符1.1 特殊标识符1.1.1 "<" 和 ">"&#xff08;<&#xff1b;&#xff09;1.1.2 空格&#xff08;&emsp&#xff1b;&#xff09;1.1.3 商…

uniapp微信小程序无法使用本地静态资源图片,背景图在真机不显示方法

前言 首先要说明&#xff0c;使用HBuilder或者vs Code工具开发的时候&#xff0c;在微信开发者工具调试的时候&#xff0c;我们使用本地图片是OK的&#xff0c;但是一旦放到真机上调试的时候&#xff0c;图片就显示不出来。 先看uniapp官网对背景图片的说明 错误用法 <tem…

uniapp 微信小程序和H5的弹窗滚动穿透解决

滚动穿透&#xff1a; 页面里的弹窗也是可以滚动的&#xff0c;然后页面本身内容多所以也是滚动的&#xff0c;就造成&#xff0c;在弹窗滚动的时候&#xff0c;页面内容也跟着滚动了。如图所示 ps: 电脑端分鼠标滚轮滚动和长按鼠标拖拽滚动&#xff0c;手机端只有触屏滑屏滚…

视频实时行为检测——基于yolov5+deepsort+slowfast算法

文章目录前言一、核心功能设计二、核心实现步骤1.yolov5实现目标检测2.deepsort实现目标跟踪3.slowfast动作识别三、核心代码解析1.参数2.主函数3.将结果保存成视频总结前言 前段时间打算做一个目标行为检测的项目&#xff0c;翻阅了大量资料&#xff0c;也借鉴了不少项目&…

【Java】运算符

我不去想是否能够成功 既然选择了远方 便只顾风雨兼程 —— 汪国真 目录 1. 认识运算符 1.1 认识运算符 1.2 运算符的分类 2. 算术运算符 2.1 四则运算符 2.2 复合赋值运算符 2.3 自增 / 自减 运算符 3.关系运算符 4.逻辑运算符 4.1 逻辑与 && 4.2 逻…

什么是异步

文章目录 前言一、异步是什么&#xff1f;二、举个例子来理解异步 1.异步最典型的例子就是“回调函数”总结前言 在vue的过程中&#xff0c;我们一定会遇到诸如&#xff1a; function&#xff08;参数&#xff09;.then(res>{}) 形式的代码。到底怎么编译执行的呢 &#xf…

【Jetpack】ViewModel 架构组件 ( 视图 View 和 数据模型 Model | ViewModel 作用 | ViewModel 生命周期 | 代码示例 | 使用注意事项 )

文章目录一、Activity 遇到的问题二、视图 View 和 数据模型 Model三、ViewModel 架构组件作用四、ViewModel 代码示例1、ViewModel 视图模型2、Activity 组件3、UI 布局文件4、运行效果五、ViewModel 生命周期六、ViewModel 使用注意事项一、Activity 遇到的问题 Activity 遇到…

宝塔部署nodejs项目

前言 部署操作很简单&#xff0c;网上也有很多教程&#xff0c;不过我还是踩坑了&#xff0c;这里记录一下&#xff0c;给其他人也避避坑吧。 步骤 首先你已经有了服务器&#xff0c;并且打开了宝塔面板&#xff0c;其次准备好你的nodejs项目。 在宝塔安装pm2管理器&#xf…

Nginx 调整文件上传大小限制

使用3A服务器做了网页&#xff0c;感觉挺不错的&#xff0c;使用LNMP环境 用Nginx部署了前端&#xff0c;发现上传附件大一点就会报错&#xff0c;查看配置文件&#xff0c;发现spring的附件配置已经配置了。那么就看下Nginx的body设置。nginx文件上传默认是1MB。 在 server 模…

VUE3TS: Vue3+TS的项目搭建

简介 通过 Vue-cli4 创建的 Vue3TS 的项目&#xff0c;并进行一些基础使用的举例。 此例是以 VSCode编辑器 进行的编码。 一、项目搭建 1. 进入命令提示符窗口 在要搭建项目的文件夹中&#xff0c;点击路径&#xff0c;输入CMD并按回车 2. 查看node版本、Vue-cli版本 2…

Android 架构之长连接技术

上文中我们提到了HttpDNS&#xff0c;虽然它比系统DNS更优&#xff0c;但终归还是要做DNS操作。而长连接都是IP直接连接&#xff0c;因此没有DNS相关的开销和耗时。 3. 如果有大量网络请求&#xff0c;可以明显减少网络延时&#xff0c;节省带宽 对于大型App而言&#xff0c;…

npm——安装、卸载与更新

npm 官方文档&#xff1a;https://docs.npmjs.com/ 什么是npm npm&#xff08;“Node 包管理器”&#xff09;是 JavaScript 运行时 Node.js 的默认程序包管理器。 它也被称为“Ninja Pumpkin Mutants”&#xff0c;“Nonprofit Pizza Makers”&#xff0c;以及许多其他随机…

Vue通知提醒框(Notification)

项目相关依赖版本信息 可自定义设置以下属性&#xff1a; 自动关闭的延时时长&#xff08;duration&#xff09;&#xff0c;单位ms&#xff0c;默认4500ms消息从顶部弹出时&#xff0c;距离顶部的位置&#xff08;top&#xff09;&#xff0c;单位像素px&#xff0c;默认24p…

WebSocket开发(心跳监测)功能

前言 在之前的文章中完成了客服对话的Demo功能&#xff0c;但是现在的连接是无限制的长时间连接没有做心跳、失活、超时断连等功能&#xff0c;心跳的实现方法有很多种&#xff0c;并且WebSocket就提供了ping/pong类型的消息。 心跳的触发方式也分两种&#xff1a; 客户端触…

微信小程序实现图片上传

前言 手机上传图片的功能大家一定都用过吧&#xff0c;今天教你用微信小程序实现这个小功能。 实现效果如下&#xff1a; 实现思路&#xff1a; 首先我们需要定义一个存放图片的数组&#xff0c;通过方法拿取图片的详细信息&#xff0c;然后调用微信小程序的 wx.uploadFile 方…

element ui datepicker时间控件实现范围选择周,季,年。

因项目要求&#xff0c;需日&#xff0c;周&#xff0c;月&#xff0c;季&#xff0c;年五种日期范围选择器&#xff0c;故参考文章&#xff08;在末尾&#xff09;后分享 一.效果图 二、版本及下载 1.实现需要修改源码&#xff0c;目前修改的版本为2.15.3&#xff0c;所以想要…

Vue3路由配置createRouter、createWebHistory、useRouter,useRoute

目录 手动配置Vue-router环境&#xff1a; 组件内部跳转路由、传参useRouter,useRoute Vue3和Vue2基本差不多&#xff0c;只不过需要将createRouter、createWebHistory从vue-router中引入&#xff0c;再进行使用。 手动配置Vue-router环境&#xff1a; 1、下载包&#xff1a…