webSocket实现多人在线聊天

主要思路如下:

1.使用vue构建简单的聊天室界面

2.基于nodeJs 的webSocket开启一个socket后台服务,前端使用H5的webSocket来创建一个socket对象来链接后端的socket服务

3.后端开启一个socket服务后,可以监听到客户端的链接,以及客户端发送过来的消息;也可以主动给客户端发送消息,例如:当有新的客户端连接的时候,服务端主动给所有连接的客户发消息,让所有人都知道有新的用户加入聊天室

4.前端new一个webSocket实例,去连接socket服务,然后客户端和服务端就可以实现双向通讯了,连接到后端服务之后,我们就可以向服务端发送消息了,并且也可以监听到服务端发送过来的消息。

5.双向通讯建立起来之后,可以根据功能需求来进行相应的代码逻辑

一、使用vue构建一个简单的项目,并开发一个简单的聊天页面

聊天页面代码,前端的主要逻辑都在这里 chating/index.vue

<template>
<div class="chating">
<div class="chating-wrap">
<div class="title">聊天页面</div>
<div class="chating-content">
<div class="chating-body">
<div class="chating-list">
<ul class="chating-records">
<div :key="index" v-for="(item, index) in chatingRecords">
<li class="other" v-show="item.nickName != myNickName">
<img alt="用户头像" src="../../assets/logo.png" />
<div class="record-text-wrap">
<div class="nick-name">{{item.nickName}}</div>
<div class="record-text">{{item.message}}</div>
</div>
</li>
<li class="my" v-show="item.nickName == myNickName">
<div class="record-text-wrap">
<!-- <div class="nick-name">迷离</div> -->
<div class="record-text">{{item.message}}</div>
</div>
<img alt="用户头像" src="../../assets/logo.png" />
</li>
</div>
</ul>
</div>
<div class="chating-btns">
<input class="input-text" placeholder="请输入聊天内容" type="text" v-model="text" />
<button @click="sendData" class="send">发送</button>
</div>
</div>
<div class="chating-online-number">
<div class="online-num">在线用户{{userList.length}}</div>
<ul v-if="userList.length > 0">
<li :key="index" class="user" v-for="(item, index) in userList">
<img alt="用户头像" src="../../assets/logo.png" />
<span>{{item.userName}}</span>
</li>
</ul>
<button @click="loginOutHandler">退出群聊</button>
</div>
</div>
</div> <div class="login" v-if="showLogin">
<div class="opacity-wrap">
<div>
用户名:
<input class="user-name" v-model="userName" />
</div>
<button @click="loginHandler" class="login-btn">登录</button>
</div>
</div>
</div>
</template> <script>
export default {
data () {
return {
text: '',
socketUrl: 'ws://localhost:8888?userName=', // socket服务地址
client: null, // webSocket实例
chatingRecords: [], // 聊天记录
myNickName: '', // 是否是自己
userName: '',
showLogin: false,
userList: []
}
},
created () {
console.log('created')
// this.initChaing()
},
mounted () {
console.log('mounted')
},
methods: {
/* 初始化聊天,连接socket */
initChaing () {
let that = this
if (window.WebSocket) {
/* webSocket 连接服务器 */
this.client = new WebSocket(this.socketUrl + this.myNickName) /* 监听客户端连接 */
this.client.onopen = function (ev) {
if (ev.type === 'open') {
console.log('客户端连接socket服务')
}
} /* 监听服务端发送的消息 */
this.client.onmessage = function (ev) {
let data = JSON.parse(ev.data)
/* 用户在线信息接收的是一个jsony数组 */
if (data instanceof Array === true) {
that.userList = data // 在线用户数量变化
} else {
/* 聊天信息接收的是一个json对象 */
that.chatingRecords.push(data) // 在线用户聊天
}
} /* 监听服务端关闭 */
this.client.onclose = function (ev) {
console.log('socket服务已关闭')
that.client = null // 客户端或者是服务端断开后,将webSocket实例清除
} /* 监听服务端异常 */
this.client.onerror = function () {
if (!that.client) {
console.log('socket服务连接失败')
}
that.loginOutHandler()
}
} else {
alert('该浏览器不支持webSocket,请使用主流浏览器,如chrome')
}
},
loginHandler () {
this.myNickName = this.userName
this.showLogin = false
/* 登录成功后再连接服务,是为了连接服务的时候把用户信息发过去 */
this.initChaing()
},
loginOutHandler () {
this.client.close()
this.client = null // 客户端或者是服务端断开后,将webSocket实例清除
this.$router.push('/')
},
sendData () {
if (!this.myNickName) {
alert('请登录')
this.showLogin = true
return
} let data = {
nickName: this.myNickName,
uid: new Date().getTime(),
message: this.text,
date: new Date()
}
if (this.client) {
this.client.send(JSON.stringify(data))
this.text = ''
} else {
console.log('socket服务连接失败,正在重新连接服务..')
this.initChaing()
}
}
},
beforeDestroy () {
this.client.close()
}
}
</script> <style lang="">
.login {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: center;
align-items: center;
}
.opacity-wrap {
width: 500px;
height: 300px;
background: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.user-name {
font-size: 16px;
padding: 5px;
text-indent: 10px;
}
.login-btn {
font-size: 20px;
background: cornflowerblue;
color: 20px;
margin-top: 30px;
color: #fff;
border: none;
outline: none;
padding: 10px 20px;
border-radius: 10px;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
.chating {
max-width: 800px;
border: 20px solid lightcyan;
border-radius: 20px;
margin: 0 auto 0;
}
.title {
background: cornflowerblue;
color: #fff;
padding: 5px 0 5px;
}
.chating-content {
width: 100%;
display: flex;
justify-content: space-between;
}
.chating-body {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
background: #f3f3f3;
}
.chating-list {
flex: 1;
border: 1px solid cornflowerblue;
}
.chating-records {
padding: 10px;
min-height: 300px;
max-height: 600px;
overflow-y: auto;
}
.chating-records li {
margin-bottom: 20px;
}
.chating-records .other {
display: flex;
justify-content: start;
align-items: flex-start;
}
.chating-records .my {
display: flex;
justify-content: flex-end;
align-items: center;
} .chating-records img {
width: 36px;
height: 36px;
/* border-radius: 50%; */
margin-right: 15px;
background: purple;
}
.chating-records .my img {
margin-right: 0;
margin-left: 15px;
}
.chating-records .other .record-text-wrap {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.chating-records .my .record-text-wrap {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.nick-name {
font-size: 14px;
margin-bottom: 5px;
color: #666;
}
.record-text {
max-width: 260px;
text-align: left;
font-size: 14px;
padding: 5px;
background: #fff;
border-radius: 5px;
} .chating-btns {
background: burlywood;
padding: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.input-text {
font-size: 16px;
border: none;
outline: none;
padding: 5px 0 5px 5px;
}
.send {
font-size: 16px;
border: none;
outline: none;
padding: 4px 15px;
margin-left: 20px;
} .online-num {
font-size: 12px;
padding-bottom: 15px;
}
.chating-online-number {
padding: 15px;
height: 100%;
}
.chating-online-number ul {
list-style: none;
margin: 0;
padding: 0;
min-width: 120px;
max-height: 580px;
overflow-y: auto;
}
.user {
display: flex;
justify-content: space-between;
align-content: center;
line-height: 20px;
font-size: 12px;
border-bottom: 1px solid aqua;
padding: 10px;
margin-bottom: 5px;
}
.user img {
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 5px;
background: palevioletred;
}
</style>

二、使用nodeJs搭建一个webSocket服务

webSocket.js文件

/* 首先在根目录下安装nodeJs的ws模块:npm install ws */

/* 引入nodejs的webSocket模块 */
var WebSocket = require('ws').Server
var moment = require('moment') /* 创建一个webSocket实例 */
var wss = new WebSocket({
url: 'localhost', // webSocket服务的ip
port: 8888 // webSocket服务的端口
}) /* socketId */
var id = 0
var onlineMemberList = []
var defaultUser = 'user' /* 监听客户端连接 */
wss.on('connection', function (ws, req) {
id++
ws.id = id // 给每个连接的客户端绑定一个id
let reqUser = req.url.split('?')[1]
let name = reqUser && reqUser.split('=')[1]
let userName
if (name) {
/* 因为传过来的名字可能是中文的,这里需要解码,否则前端接收到的是一堆编码后的字符串 */
userName = decodeURIComponent(name)
} else {
userName = defaultUser + id
}
var userInfo = {
userName: userName,
socketId: id,
date: moment().format('MMMM Do YYYY, h:mm:ss a')
}
/* 当用户名一样的时候,表示重新登录 */
for (var i = 0; i < onlineMemberList.length; i++) {
if (userInfo.userName === onlineMemberList[i].userName) {
onlineMemberList[i] = userInfo
wss.clients.forEach(itemWs => {
itemWs.send(JSON.stringify(onlineMemberList))
})
return
}
} onlineMemberList.push(userInfo)
wss.clients.forEach(itemWs => {
itemWs.send(JSON.stringify(onlineMemberList))
}) /* 监听客户端发过来的信息 */
ws.on('message', function (data) {
console.log(data)
let newData = JSON.parse(data)
newData.serveDate = moment().format('MMMM Do YYYY, h:mm:ss a')
/* 给所有连接的客户端发送数据 */
wss.clients.forEach(itemWs => {
itemWs.send(JSON.stringify(newData))
}) /* 给最后一个连接的客户端发送数据 */
// ws.send(JSON.stringify(newData))
}) /* 监听客户端关闭 */
ws.on('close', function (ev) {
console.log('客户端断开连接')
/* 监听到用户断开连接后,将在线的重新广播给所有有人 */
onlineMemberList = onlineMemberList.filter(item => {
return item.socketId !== ws.id
}) wss.clients.forEach(itemWs => {
itemWs.send(JSON.stringify(onlineMemberList))
})
console.log(onlineMemberList, 'onlineMemberList')
console.log(ws.id, 'ws.id')
}) /* 监听客户端发生异常 */
ws.on('error', function (ve) {
console.log('客户端异常')
})
}) console.log('webSocket服务已开启,端口为:8888')

为了方便管理代码,我把webSocket服务的代码放到了vue项目根目录下的socketServe文件夹下面,在启动vue项目之前先要来到这个文件夹里面开启socket服务,才能在聊天界面连接到服务器

github地址:https://github.com/yanhuomili/chatingGroup

 

webSocket实现多人聊天功能的更多相关文章

  1. WebSocket(3)---实现一对一聊天功能

    实现一对一聊天功能 功能介绍:实现A和B单独聊天功能,即A发消息给B只能B接收,同样B向A发消息只能A接收. 本篇博客是在上一遍基础上搭建,上一篇博客地址:[WebSocket]---实现游戏公告功能 ...

  2. Spring Websocket实现简易在线聊天功能

    针对Spring Websocket的实现,我参照了其他博主的文章https://www.cnblogs.com/leechenxiang/p/5306372.html 下面直接给出实现: 一.引入相 ...

  3. WebSocket 和 Golang 实现聊天功能

    http://www.open-open.com/lib/view/open1416379948711.html 这个示例应用程序展示了如何使用 WebSocket, Golang 和 jQuery  ...

  4. Spring 学习——基于Spring WebSocket 和STOMP实现简单的聊天功能

    本篇主要讲解如何使用Spring websocket 和STOMP搭建一个简单的聊天功能项目,里面使用到的技术,如websocket和STOMP等会简单介绍,不会太深,如果对相关介绍不是很了解的,请自 ...

  5. 使用websocket实现在线聊天功能

    很早以前为了快速达到效果,使用轮询实现了在线聊天功能,后来无意接触了socket,关于socket我的理解是进程间通信,首先要有服务器跟客户端,服务的启动监听某ip端口定位该进程,客户端开启socke ...

  6. spring boot集成websocket实现聊天功能和监控功能

    本文参考了这位兄台的文章: https://blog.csdn.net/ffj0721/article/details/82630134 项目源码url: https://github.com/zhz ...

  7. 基于vs2015 SignalR开发的微信小程序使用websocket实现聊天功能

    一)前言 在微信小程上实现聊天功能,大致有三种方式:1)小程序云开发 2)购买第三方IM服务 3)使用自己的服务器自己开发. 这里重要讲使用自己的服务器自己开发,并且是基于vs的开发. 网上提供的解决 ...

  8. JAVA结合WebSocket实现简单客服聊天功能

    说明:该示例只简单的实现了客服聊天功能. 1.聊天记录没有保存到数据库中,一旦服务重启,消息记录将会没有,如果需要保存到数据库中,可以扩展 2.页面样式用的网上模板,样式可以自己进行修改 3.只能由用 ...

  9. WebSocket(5)---多人聊天系统

    多人聊天系统 功能说明:多人聊天系统,主要功能点: 1.当你登陆成功后,可以看到所有在线用户(实际开发可以通过redis实现,我这边仅仅用map集合) 2.实现群聊功能,我发送消息,大家都可以看到. ...

随机推荐

  1. centos 7查看系统网络情况netstat

    查看系统网络情况 netstat ➢ 基本语法 netstat [选项] ➢ 选项说明 -an 按一定顺序排列输出 -p 显示哪个进程在调用 应用案例 请查看服务名为 sshd 的服务的信息. ➢ N ...

  2. 白日梦的MySQL专题(第38篇文章)8分钟回顾MySQL的索引

    目录 公众号首发-推荐阅读原文-格式更好看 一.导读 二.聚簇索引 三.二级索引 四.联合索引 4.1.什么是联合索引 4.2.左前缀原则 4.3.联合索引的分组&排序 五.覆盖索引 六.倒排 ...

  3. Redis 面试题 - 收藏版 (持续更新、吐血推荐)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...

  4. noip模拟7[匹配·回家·寿司]

    这次考试状态好像还是没有回来,只拿了55pts,还全是第一题的功劳,就是一个小KMP,然后还让我给打错了 就很难受,while打成了if,然后wa掉45分考完立马拿回来了,悔死了,害 第二题爆零了,为 ...

  5. 【题解】入阵曲 luogu3941 前缀和 压维

    丹青千秋酿,一醉解愁肠. 无悔少年枉,只愿壮志狂 题目 题目描述 小 F 很喜欢数学,但是到了高中以后数学总是考不好. 有一天,他在数学课上发起了呆:他想起了过去的一年.一年前,当他初识算法竞赛的 时 ...

  6. Redundant Paths 分离的路径

    Redundant Paths 分离的路径 题目描述 为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们已经厌倦了被迫走某一条路,所以她 ...

  7. 05 找出占用CPU、内存过高的进程

    #!/bin/bash export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin echo "----- ...

  8. 16、mysql主从复制问题总结

    16.1.主库"show master status"没有结果: 1.原因: 主库binlog功能开关没有改或没有生效: 2.解决办法: (1)[root@backup ~]#eg ...

  9. Redis启动正常,一段时间后报错,连不上redis

    Redis报错 1.redis在最终目标上移动临时数据库文件时出错 错误:redis:Error moving temp DB file temp-13792.rdb on the final des ...

  10. 详解Docker 端口映射与容器互联

    详解Docker 端口映射与容器互联 1.端口映射实现访问容器 1.从外部访问容器应用 在启动容器的时候,如果不指定对应的参数,在容器外部是无法通过网络来访问容器内部的网络应用和服务的. 当容器中运行 ...