socket.io 实现多人聊天室

网上看到这篇文章觉得挺有意思,看到这个效果图,于是决定自己实现一下。参考文章

一、参考效果图

二、设计思路图

三、代码实现

1.初始化项目,下载需要的依赖包,以下是整个项目的依赖文件

npm init
npm i koa koa-router koa-static koa-views socket.io -S

2.注册逻辑,前端是简单的 form 表单提交,但是这里注意,有文件上传,Content-Type 需改为’multipart/form-data’,服务器代码如下,连接了 mysql 数据库,把用户注册信息存至 student 表中

const Router = require('koa-router');
const router = new Router();const multer = require('@koa/multer');
const upload = multer({ dest: 'public/imgs' });
const conn = require('../config/db.config');router.get('/', async (ctx, next) => {await ctx.render('register');
});router.post('/', upload.single('avatar'), async (ctx, next) => {const { username, password } = ctx.request.body;const avatar = `/imgs/${ctx.file.filename}`;const connection = await conn();try {const [rows,] = await connection.execute('insert into student(name,password,avatar) values(?,?,?)',[username, password, avatar]);if (rows?.affectedRows) {ctx.body = {code: 0,msg: '注册成功',};}} catch (error) {ctx.body = {code: -1,msg: '注册失败,该用户已存在',};}
});module.exports = router;

3.登录逻辑,前端是简单的 form 表单提交,服务器代码如下

const Router = require('koa-router');
const router = new Router();const conn = require('../config/db.config');
const JWT = require('../utils/jwt');router.get('/', async (ctx, next) => {await ctx.render('login');
});router.post('/', async (ctx, next) => {const { username, password } = ctx.request.body;const connection = await conn();const [rows,] = await connection.execute('select * from student where name = ? and password = ?',[username, password]);if (rows.length) {const payload = {username,password,};const token = JWT.generate(payload, '1d');ctx.set('authorization', token);ctx.body = {code: 0,msg: '登录成功',};} else {ctx.body = {code: -1,msg: '用户名或密码错误',};}
});module.exports = router;

4.JWT 登录鉴权:前端在登录成功后往 localStorage 中存储服务器发送的 token,然后在每次发起请求前都把 token 带到请求头里,服务器使用应用级中间件统一处理所有接口的时候首先校验 token 是否有效,有则成功返回数据,无则返回 401。我在这里用到了 jsonwebtoken 第三方插件,封装的一个函数用于加密与解密

const jwt = require('jsonwebtoken');const JWT = {generate: (payload, expiresTime) => {// 签名return jwt.sign(payload, 'anydata', { expiresIn: expiresTime });},verify: (token) => {// 解密try {return jwt.verify(token, 'anydata');} catch (error) {return false;}},
};module.exports = JWT;

5.由于我写的聊天室页面没有别的 ajax 请求,只有 websocket 请求,因此我把 token 校验直接写在了 websocket 连接内了。如果有 ajax 请求,还是需要写到整个项目所有路由的前面做一个拦截。 以下是 webSocket 服务端推送数据的代码

const JWT = require('./utils/jwt');
const conn = require('./config/db.config');const webSocketType = {Error: 0,GroupChat: 1, // 群聊GroupList: 2, // 用户列表SingleChat: 3, // 私聊ChatList: 4, // 聊天记录
};// 封装统一的发送格式
function createWebSocketInfo(user, data, avatar, sendTime) {return {user,data,avatar,sendTime,};
}// 发送用户列表
function sendAllList(io) {const userList = Array.from(io.sockets.sockets).map((item) => item[1].user);io.sockets.emit(webSocketType.GroupList, createWebSocketInfo(null, userList));
}let allData = [];function createWebSocketServer(server) {const socketio = require('socket.io');const io = socketio(server);io.on('connection', async (socket) => {const payload = JWT.verify(socket.handshake.query.token);if (payload) {socket.user = payload;sendAllList(io);socket.emit(webSocketType.ChatList, createWebSocketInfo(null, allData));} else {socket.emit(webSocketType.Error, createWebSocketInfo(null, 'token失效'));}socket.on('disconnect', () => {sendAllList(io);allData = [];});socket.on(webSocketType.GroupChat, async (msg) => {// 告诉客户端是谁发的消息,以及从数据库中找出发送的这个人的头像const connection = await conn();const [rows,] = await connection.execute('select avatar from student where name = ?',[socket.user.username]);io.sockets.emit(webSocketType.GroupChat,createWebSocketInfo(socket.user.username,msg.data,rows[0].avatar,msg.sendTime));// 保存所有的聊天记录allData.push(createWebSocketInfo(socket.user.username,msg.data,rows[0].avatar,msg.sendTime));});});
}module.exports = createWebSocketServer;

6.客户端发送消息

<!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>聊天室</title><link rel="stylesheet" href="/css/chat.css" /><script src="/js/axios.js"></script><script src="/js/socketio.js"></script><script src="/js/jquery.js"></script></head><body><div class="user">当前用户:<span id="user"></span></div><div class="title"><span id="newComingUsers">xxx</span>&nbsp;的群聊</div><div class="container"><div class="chatList" id="chatList"><!-- 右边 --><!-- <div class="chatItem chatItemRight"><span class="text">你好</span><p><img src="/imgs/qiu.jpg" alt="avatar" /></p></div> --><!-- 左边 --><!-- <div class="chatItem chatItemLeft"><p><img src="/imgs/qin.jpg" alt="avatar" /></p><p class="otherUserInfo"><span class="otherUser">梁泽钦</span><span class="text">你好</span></p></div> --></div></div><div class="inputBox"><input type="text" id="text" class="text" /><input type="button" id="send" class="send" value="发送" /></div></body><script>const user = document.getElementById('user');user.innerHTML = localStorage.getItem('user');const newComingUsers = document.getElementById('newComingUsers');const send = document.getElementById('send');const text = document.getElementById('text');const chatList = document.getElementById('chatList');// 消息的类型const webSocketType = {Error: 0,GroupChat: 1,GroupList: 2,SingleChat: 3,ChatList: 4,};// 封装发送消息的格式function createWebSocketInfo(user, data, sendTime) {return {user,data,sendTime,};}const renderLeft = ({ avatar, user, data }) => {// 展示在左边$('#chatList').append(`<div class="chatItem chatItemLeft"><p><img src="${avatar}" alt="avatar" /></p><p class="otherUserInfo"><span class="otherUser">${user}</span><span class="text">${data}</span></p></div>`);};const renderRight = ({ avatar, user, data }) => {// 展示在右边$('#chatList').append(`<div class="chatItem chatItemRight"><span class="text">${data}</span><p><img src="${avatar}" alt="avatar" /></p></div>`);};// 1.连接socket服务器const socket = io(`ws://localhost:3000?token=${localStorage.getItem('authorization')}`);// 2.监听连接成功事件,打印xxx连接成功// 3.监听用户列表事件,获取所有连接上聊天室的用户列表,展示在newComingUserssocket.on(webSocketType.GroupList, (msg) => {const { data } = msg;newComingUsers.innerHTML = '';newComingUsers.innerHTML = data.map((item) => item.username).join(',');});// 4.监听聊天记录事件socket.on(webSocketType.ChatList, (msg) => {const { data: allData } = msg;const currentTime = Date.now();const currentUser = localStorage.getItem('user');allData.forEach((item) => {const { user, data, avatar, sendTime } = item;if (sendTime < currentTime) {if (user === currentUser) {renderRight(item);} else {renderLeft(item);}}});});// 5.监听连接错误事件,token失效socket.on(webSocketType.Error, (msg) => {localStorage.removeItem('authorization');location.href = '/login';});// 6.监听群聊事件,判断推送过来的消息是当前用户所发-展示右边,非当前用户所发-展示左边socket.on(webSocketType.GroupChat, (msg) => {const { user, data, avatar } = msg;const currentUser = localStorage.getItem('user');if (user === currentUser) {renderRight(msg);} else {renderLeft(msg);}});// 7.群聊,客户端发送消息,发送完成清空send.onclick = () => {socket.emit(webSocketType.GroupChat,createWebSocketInfo(null, text.value, Date.now()));text.value = '';};</script>
</html>
/* chat.css */
* {margin: 0;padding: 0;
}html,
body {height: 100%;
}body {margin: 20px 0 0 20px;width: 600px;
}.user {width: 100%;display: flex;justify-content: flex-end;margin-bottom: 4px;
}.title {height: 100%;background: #000;display: flex;justify-content: center;align-items: center;color: #ffffff;font-size: 22px;font-weight: bold;width: 100%;height: 50px;
}.container {height: 700px;background: #f1d4d4;width: 100%;overflow-y: auto;
}.chatList {margin-top: 15px;
}.chatItem {display: flex;margin-bottom: 20px;
}.chatItemRight {justify-content: flex-end;
}.chatItem .text {display: inline-block;padding: 0 6px;border-radius: 4px;display: flex;justify-content: center;align-items: center;max-width: 300px;min-height: 40px;
}.chatItemRight > .text {background: #62b900;
}.chatItemLeft .text {background: #ffffff;
}.chatItemLeft {display: flex;
}.chatItemLeft .otherUser {display: inline-block;margin-bottom: 4px;
}.chatItem img {width: 40px;height: 40px;margin: 0 10px;
}.inputBox {display: flex;width: 100%;height: 50px;
}.inputBox .text {width: 540px;
}.inputBox .send {width: 60px;cursor: pointer;
}

四、最终效果展示



socket.io实现多人聊天相关推荐

  1. node php聊天室,利用socket.io实现多人聊天室(基于Nodejs)

    利用socket.io实现多人聊天室(基于Nodejs) socket.io简介 在Html5中存在着这样的一个新特性,引入了websocket,关于websocket的内部实现原理可以看这篇文章,这 ...

  2. php socket多人聊天,socket.io实现多人聊天

    1. 后端环境搭建 # npm init # npm install -s express # npm install -s socket.io npm init 会生成json文件作为依赖包,exp ...

  3. ajax 多人聊天吧,基于Nodejs利用socket.io实现多人聊天室

    socket.io简介 在Html5中存在着这样的一个新特性,引入了websocket,关于websocket的内部实现原理可以看这篇文章,这篇文章讲述了websocket无到有,根据协议,分析数据帧 ...

  4. Java Socket实现简易多人聊天室传输聊天内容或文件

    Java Socket实现简易多人聊天室传输聊天内容或文件 Java小练手项目:用Java Socket实现多人聊天室,聊天室功能包括传输聊天内容或者文件.相比于其它的聊天室,增加了传输文件的功能供参 ...

  5. 如何使用Vue,Phaser,Node,Express和Socket.IO构建多人桌面游戏模拟器

    Putting together all of the pieces of a full stack JavaScript application can be a complex endeavor. ...

  6. phaser.min.js_如何使用Phaser 3,Express和Socket.IO构建多人纸牌游戏

    phaser.min.js I'm a tabletop game developer, and am continually looking for ways to digitize game ex ...

  7. 在线白板,基于socket.io的多人在线协作工具

    为什么80%的码农都做不了架构师?>>>    首发:个人博客,更新&纠错&回复 是昨天这篇博文留的尾巴,socket.io库的使用练习,成品地址在这里. 代码已经上 ...

  8. python实现简易聊天需要登录博客园zip下载_Python基于Socket实现简易多人聊天室的示例代码...

    前言 套接字(Sockets)是双向通信信道的端点. 套接字可以在一个进程内,在同一机器上的进程之间,或者在不同主机的进程之间进行通信,主机可以是任何一台有连接互联网的机器. 套接字可以通过多种不同的 ...

  9. 利用socket.io+nodejs打造简单聊天室

    代码地址如下: http://www.demodashi.com/demo/11579.html 界面展示: 首先展示demo的结果界面,只是简单消息的发送和接收,包括发送文字和发送图片. ws说明: ...

  10. vue和socket.io开发简单web聊天室

    2019独角兽企业重金招聘Python工程师标准>>> 效果预览 https://www.wangchunjian.top/chat.html 需要用到的库 https://sock ...

最新文章

  1. 面试AI Lab能力测评
  2. 数据库操作(使用FMDB)
  3. 秋色园QBlog技术原理解析:Web之页面处理-内容填充(八)
  4. jsp中获取不到后台请求域中的值
  5. 268. 缺失数字 golang
  6. java+txt+词语+次数_Java练习2--读取txt文件统计考勤次数并写入一个txt文件中
  7. matlab连续投影算法SPA使用示例
  8. 如何根据日期+数字生成流水号
  9. AOSP ~ 默认开启开发者模式
  10. 【深度学习模型】了解一下Faster RCNN
  11. Python turtle制作书法作品——《鸟鸣涧》
  12. 把台式计算机硬盘拆下,台式电脑的硬盘可以拆下来吗
  13. <Java>设计一个名为Fan的类
  14. oracle的mins,分钟的英文缩写,10min还是10mins!
  15. Springboot发送手机短信验证码并且校验
  16. 微服务组件之限流器与熔断器
  17. 解决:Excel 下拉项数据报 输入内容不能大于255个字符
  18. 如何用Python 求函数 y = sinx 在区间[0, pi/2]上的弧长
  19. 老司机 iOS 周报 #15 | 2018-04-16
  20. lol全队消息怎么发_lol怎么发给所有人 LOL里面怎么给所有人发送消息

热门文章

  1. C/C++后端开发学习路线总结(附带实习学习经历分享)
  2. java生成vcf_Android vcard使用示例,生成vcf文件
  3. mac brew安装/卸载
  4. css导航栏背景色透明,css如何设置背景颜色透明?css设置背景颜色透明度的两种方法介绍...
  5. 这双 Googler 设计的 Nike 鞋真的是──丑爆了
  6. SQL学习笔记(05)_JOIN的类型与用法
  7. 全球前沿技术趋势报告;华为发布Mate 40/Pro 系列新机;Windows 计算器移植到到 Linux...
  8. linux 安装xz,在Ubuntu 18.04 LTS下安装linux-5.0.8.tar.xz的方法
  9. 2018.11.07【NOIP训练】lzy的游戏(01背包)
  10. 【无标题】如何做APP客户端数据埋点