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. 每日三道面试题,通往自由的道路6——JVM

    茫茫人海千千万万,感谢这一秒你看到这里.希望我的面试题系列能对你的有所帮助!共勉! 愿你在未来的日子,保持热爱,奔赴山海! 每日三道面试题,成就更好自我 今天我们继续聊聊JVM的话题吧! 1. 那你知 ...

  2. NAT介绍与配置

    一,NAT定义 二.NAT的分类 三,NAT配置实验 一,NAT定义 NAT(Network Address Translation),网络地址转换技术,随着Internet的发展,IPv4地址枯竭已 ...

  3. GetModuleFileName函数的用法

    函数的功能 获取exe可执行文件的绝对路径. 用法 通过获取到exe的路径,可以获取到程序路径下(父路径或者子路径)的一些其它文件路径. 函数原型 DWORD WINAPI GetModuleFile ...

  4. external-provisioner源码分析(1)-主体处理逻辑分析

    更多ceph-csi其他源码分析,请查看下面这篇博文:kubernetes ceph-csi分析目录导航 概述 接下来将对external-provisioner组件进行源码分析. 在external ...

  5. Redis高并发快的3大原因详解

    1. Redis的高并发和快速的原因 1.redis是基于内存的,内存的读写速度非常快: 2.redis是单线程的,省去了很多上下文切换线程的时间: 3.redis使用多路复用技术,可以处理并发的连接 ...

  6. Maven——基础篇

    Maven--基础篇 Maven出现前的问题 一个项目就是一个工程,而工程内一般是通过package包来分模块,比较用户模块,订单模块等,如果项目过于庞大,通过包模块来划分就不太合适,而应该拆分为模块 ...

  7. 17、ansible配置管理

    17.1.前言: 1.说明: ansible是自动化运维工具,基于Python开发,实现了批量系统配置.批量程序部署.批量运行命令等功能. ansible是基于模块工作的,本身没有批量部署的能力,真正 ...

  8. Gym 101334A Area 51 数学

    大致题意: 给出n个建筑的二维坐标,每个建筑名称为一个字母,不同坐标的建筑可以有同一名称,并保证这些坐标都是在y轴上半轴.给出一串建筑名称的字符串,在X轴上找出一个或多个区间,使Nick在这个区间上从 ...

  9. Maven安装、配置及基础

    简介: Maven是Apache公司的开源项目,是项目构建工具,用来管理依赖. Maven的优点: 同样的代码实现相同的功能,Maven项目没有Jar包,项目大小更小. maven的优点如何实现: 没 ...

  10. CentOS-Docker搭建Nextcloud

    下载镜像 $ docker pull nextcloud 运行镜像 $ docker run -d --restart=unless-stopped --name nextcloud -v /home ...