网站首页 > 技术文章 正文
点击右上方红色按钮关注“web秀”,让你真正秀起来
前言
如果你看到这篇文章,还没有了解前面2篇文章的同学,可以先去了解一波,这样上手更快。 推荐文章:
《Nodejs + WebSocket简单介绍及示例 - 第一章》 《Nodejs + WebSocket + Vue 实现多人聊天室WebIM功能 - 第二章》
这篇文章都是前面文章的加强版,功能升级。给大家提供一个循序渐进的学习过程,一步一步的来。
在第二篇文章结束时,我们就已经可以一对多的聊天了,就是多人群聊。这次,我们进行扩展来实现一对一、一对多功能。
WebSocket客户端UI界面更改
有了一对一,一对多,我们就需要对直接的界面做出调整了。左边显示聊天人员列表,右边是具体消息列表。
<div class="web-im"> 
 <div class="dis-flex"> 
 <div class="user-list"> 
 <div class="user" @click="triggerGroup">群1</div> 
 <div class="user" @click="triggerPersonal(item)" v-if="item.uid!=uid" v-for="item in users">{{item.nickname}}</div> 
 </div> 
 <div class="msg-content"> 
 <div class="header im-title">{{title}}</div> 
 <div class="content im-record"> 
 <div class="li" :class="{user: item.uid == uid}" v-for="item in currentMessage"> 
 <template v-if="item.type===1"> 
 <p class="join-tips">{{item.msg}}</p> 
 </template> 
 <template v-else> 
 <div class="img">{{item.nickname}}</div> 
 <p class="message-box">{{item.msg}}</p> 
 </template> 
 </div> 
 </div> 
 <div class="footer im-input"> 
 <input type="text" v-model="msg" placeholder="请输入内容"> 
 <button @click="send">发送</button> 
 </div> 
 </div> 
 </div> 
</div> 
...
如果上方代码阅读体验太差,可以看下方图或者直接到文章最下方,点击了解更多阅读:
这里我们就写死了一个群,叫群1,默认是所用用户进去群聊。
WebSocket服务端
var ws = require("nodejs-websocket"); 
var moment = require('moment'); 
 
console.log("开始建立连接...") 
 
let users = []; 
let conns = {}; 
 
function boardcast(obj) { 
 // bridge用来实现一对一的主要参数 
 if(obj.bridge && obj.bridge.length){ 
 obj.bridge.forEach(item=>{ 
 conns[item].sendText(JSON.stringify(obj)); 
 }) 
 return; 
 } 
 server.connections.forEach((conn, index) => { 
 conn.sendText(JSON.stringify(obj)); 
 }) 
} 
 
var server = ws.createServer(function(conn){ 
 conn.on("text", function (obj) { 
 obj = JSON.parse(obj); 
 // 将所有uid对应的连接conn存到一个对象里面 
 conns[''+obj.uid+''] = conn; 
 if(obj.type===1){ 
 let isuser = users.some(item=>{ 
 return item.uid === obj.uid 
 }) 
 if(!isuser){ 
 users.push({ 
 nickname: obj.nickname, 
 uid: obj.uid 
 }); 
 } 
 boardcast({ 
 type: 1, 
 date: moment().format('YYYY-MM-DD HH:mm:ss'), 
 msg: obj.nickname+'加入聊天室', 
 users: users, 
 uid: obj.uid, 
 nickname: obj.nickname, 
 // 增加参数 
 bridge: obj.bridge 
 }); 
 } else { 
 boardcast({ 
 type: 2, 
 date: moment().format('YYYY-MM-DD HH:mm:ss'), 
 msg: obj.msg, 
 uid: obj.uid, 
 nickname: obj.nickname, 
 // 增加参数 
 bridge: obj.bridge 
 }); 
 } 
 }) 
 conn.on("close", function (code, reason) { 
 console.log("关闭连接") 
 }); 
 conn.on("error", function (code, reason) { 
 console.log("异常关闭") 
 }); 
}).listen(8001) 
console.log("WebSocket建立完毕")
如果上方代码阅读体验太差,可以看下方图或者直接到文章最下方,点击了解更多阅读:
主体结构还是和第二章类型,不同的是:
1、每次将uid对应的conn存储到一个对象conns上
2、根据客户端传入的参数bridge来判断,是群发还是一对一发送
3、群发还是第二章逻辑即可
server.connections.forEach((conn, index) => { 
 conn.sendText(JSON.stringify(obj)); 
})
4、一对一发送,bridge里面是一对一的两个用户uid,这样就可以在conns对象上找到uid对应的连接conn,并用conn发送信息即可
if(obj.bridge && obj.bridge.length){ 
 obj.bridge.forEach(item=>{ 
 conns[item].sendText(JSON.stringify(obj)); 
 }) 
 return; 
}
WebSocket客户端
export default { 
 ... 
 data(){ 
 return { 
 title: '群聊', 
 uid: '', 
 nickname: '', 
 socket: '', 
 msg: '', 
 // 当前用户所有消息 
 messageList: [], 
 users: [], 
 bridge: [] 
 } 
 }, 
 mounted() { 
 ... 
 }, 
 computed: { 
 // 当前对话渲染的msg列表 
 currentMessage() { 
 let vm = this; 
 // 筛选只有bridge相同的对话,展示出来 
 // 数组比较,先转成字符串 
 let data = vm.messageList.filter(item=>{ 
 return item.bridge.sort().join(',') == vm.bridge.sort().join(',') 
 }) 
 return data; 
 } 
 }, 
 methods: { 
 // 切换到群聊 
 triggerGroup() { 
 this.bridge = []; 
 this.title = '群聊'; 
 }, 
 // 切到具体个人 
 triggerPersonal(item) { 
 if(this.uid === item.uid){ 
 return; 
 } 
 // 将当前用户uid,和需要对话的uid放入bridge 
 this.bridge = [this.uid, item.uid]; 
 this.title = '和' + item.nickname + '聊天'; 
 }, 
 send(){ 
 if(!this.msg){ 
 return 
 } 
 this.sendMessage(2, this.msg) 
 }, 
 sendMessage(type, msg){ 
 this.socket.send(JSON.stringify({ 
 uid: this.uid, 
 type: type, 
 nickname: this.nickname, 
 msg: msg, 
 // 增加bridge参数 
 bridge: this.bridge 
 })); 
 this.msg = ''; 
 }, 
 conWebSocket(){ 
 let vm = this; 
 if(window.WebSocket){ 
 vm.socket = new WebSocket('ws://localhost:8001'); 
 let socket = vm.socket; 
 
 socket.onopen = function(e){ 
 console.log("连接服务器成功"); 
 if(!vm.uid){ 
 ... 
 } 
 // 这里将sendMessage方法if外面 
 vm.sendMessage(1) 
 } 
 socket.onclose = function(e){ 
 console.log("服务器关闭"); 
 } 
 socket.onerror = function(){ 
 console.log("连接出错"); 
 } 
 // 接收服务器的消息 
 socket.onmessage = function(e){ 
 let message = JSON.parse(e.data); 
 vm.messageList.push(message); 
 if(message.users) { 
 vm.users = message.users; 
 } 
 } 
 } 
 }, 
 login(){ 
 ... 
 } 
 } 
}
如果上方代码阅读体验太差,可以看下方图或者直接到文章最下方,点击了解更多阅读:
上方...的代码区域都是和第二篇文章一样的地方,所有就省略了。
1、默认是群发,即bridge是空数组,向所有用户发送消息
2、点击用户列表,赋予bridge当前用户uid,和需要对话的uid。
3、在第二篇文章中,渲染的消息列表是messageList。现在不是,是通过计算属性computed,只需要bridge相等的消息,得出currentMessage当前对话的消息列表
4、因为所有消息都是通过后台socket返回,也不需要考虑发送者/接收者是谁,判断bridge是否相等,可以用sort()方法排序并转换成字符串后进行对比。
快速预览效果
总结
一对一和一对多的核心,就是知道是那个用户与那个用户对话。当前目前一对多是不用考虑,因为是写死的,所有用户,后面来做不同群,随意加群聊天,就需要考虑了。也就是服务端的conn不要弄错,不然收不到消息也接收不到消息。
公告
为了感谢大家一直以来的支持,小编发起了抽奖活动,大家可以去参与,转发抽奖活动和关注小编即可参与,抽出5名小伙伴每人20元话费奖励。再次感谢大家的支持。
喜欢小编的点击关注,了解更多知识!
源码地址,可以点击下方“了解更多”获取!
猜你喜欢
- 2024-10-02 nodejs中使用sqlite3数据库(nodejs连接mysql数据库)
 - 2024-10-02 NodeJS & Dapr Javascript SDK 官方使用指南
 - 2024-10-02 专门为前端工程师设计 Nodejs+React 实战开发区块链“慕课”DApp
 - 2024-10-02 若依nodejs全栈(四:用户列表增删改查接口的实现)
 - 2024-10-02 系统性学习(3) Node.js——手写 Events
 - 2024-10-02 完美支持Vue3,一个自带管理模板的Vue3开源组件库——vuestic
 - 2024-10-02 GitHub精选 | 后台权限管理系统(基于Node.js)
 - 2024-10-02 若依nodejs全栈(三:用户信息和路由接口的实现)
 - 2024-10-02 技术开发者应该如何构建小团队的微服务方案?
 - 2024-10-02 全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析
 
- 最近发表
 - 
- 聊一下 gRPC 的 C++ 异步编程_grpc 异步流模式
 - [原创首发]安全日志管理中心实战(3)——开源NIDS之suricata部署
 - 超详细手把手搭建在ubuntu系统的FFmpeg环境
 - Nginx运维之路(Docker多段构建新版本并增加第三方模
 - 92.1K小星星,一款开源免费的远程桌面,让你告别付费远程控制!
 - Go 人脸识别教程_piwigo人脸识别
 - 安卓手机安装Termux——搭建移动服务器
 - ubuntu 安装开发环境(c/c++ 15)_ubuntu安装c++编译器
 - Rust开发环境搭建指南:从安装到镜像配置的零坑实践
 - Windows系统安装VirtualBox构造本地Linux开发环境
 
 
- 标签列表
 - 
- cmd/c (90)
 - c++中::是什么意思 (84)
 - 标签用于 (71)
 - 主键只能有一个吗 (77)
 - c#console.writeline不显示 (95)
 - pythoncase语句 (88)
 - es6includes (74)
 - sqlset (76)
 - apt-getinstall-y (100)
 - node_modules怎么生成 (87)
 - chromepost (71)
 - flexdirection (73)
 - c++int转char (80)
 - mysqlany_value (79)
 - static函数和普通函数 (84)
 - el-date-picker开始日期早于结束日期 (76)
 - js判断是否是json字符串 (75)
 - c语言min函数头文件 (77)
 - asynccallback (87)
 - localstorage.removeitem (77)
 - vector线程安全吗 (73)
 - java (73)
 - js数组插入 (83)
 - mac安装java (72)
 - 无效的列索引 (74)
 
 
