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


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


    在这里插入图片描述


    🔥前言

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

    实现效果

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


    前台核心代码


    设置不同状态

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

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

    封装一个发送信息函数

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

    function createMessage(type, data, to) {
        return JSON.stringify({
            type,
            data,
            to
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    后台核心代码

    获取到token

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

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

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

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

    function createMessage(type, user, data) {
        return JSON.stringify({
            type: type,
            user: user,
            data: data
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

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

    封装一个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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    校验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过期'))
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    前台实现(所有代码)

    <!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 = `` + onlineList.map(item => `
                            ">${item.username}
                        `).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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    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))))
            }
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    小结

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

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


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


    在这里插入图片描述

  • 相关阅读:
    空杯心态。
    科技云报道:分布式存储红海中,看天翼云HBlock如何突围?
    Netty inEventLoop方法的思想延伸
    MCDF实验5:凛冬的寒风(从verilog到SV的入门lab5)
    【三种修改 list 的方法测试】
    C++入门——域作用符,命名空间的讲解
    查看和分析 IIS 日志文件以增强 Web 服务器安全性
    药物化合物信息查询系统(精选常用的5个)
    Spring MVC 工作流程源码分析
    Gitlab服务器切换来版本升级,执行漏洞修复
  • 原文地址:https://blog.csdn.net/m0_52040370/article/details/127854142