在线聊天模实现
项目是基于node.js服务器搭建的简易双向通信网页,实现了实时更新在线人数以及用户间即时通讯的功能。
1、websocket连接创建
基于后台websocket地址创建简易的连接,并且当websocket断开后自动重新连接。
let lockReconnect = false // 是否允许重新连接
let timeout = 1000 * 20 // 心跳间隔
let reTimeout = 1000 * 10 // 重连间隔
let timeoutTimer = null // 心跳定时器
let reTimeoutTimer = null // 重连定时器
let webSocket = null
let uid = new Date().getTime() // 图方便,将时间戳当作用户id
// 创建websocket连接
function createWebSocket () {
let webSocket = new WebSocket('ws://localhost:5001/' + uid)
webSocket.binaryType = 'arraybuffer';
webSocket.onerror = function () {
console.log('连接失败...')
reConnect()
}
// websocket收到信息的处理逻辑
webSocket.onmessage = function (event) {
if (event.data) {
let res = JSON.parse(event.data)
console.log(res)
}
}
webSocket.onclose = function (event) {
console.log('连接已关闭...')
console.log('websocket 断开: ' + event.code + ' ' + event.reason + ' ' + event.wasClean)
reConnect()
}
}
// 重连
function reConnect () {
if (lockReconnect) {
return
}
lockReconnect = true
// 没连接上会一直重连,设置延迟避免请求过多
reTimeoutTimer && clearTimeout(reTimeoutTimer)
reTimeoutTimer = setTimeout(function () {
createWebSocket()
lockReconnect = false
}, reTimeout)
}
2、基于websocket动态获取在线用户
想要实现实时获取在线用户列表,并且有用户连接上wesocket服务时,通知给其他在线用户。
服务器端
// 存储在线用户
let onlineUser = []
wss.on('connection', (ws, req, res) => {
// 获取当前连接websocket的用户uid
let uid = req.url.substr(1)
let index = onlineUser.findIndex(val => val.id === uid)
if (index !== -1) {
onlineUser[index].websocket = ws
} else {
onlineUser.push({
websocket: ws,
id: uid
})
}
// 获取在线用户id数组
let User = JSON.stringify({
onlineUser: onlineUser.map(val => val.id),
type: 'users'
})
// 通知所有在线用户有新用户上线
onlineUser.forEach((client) => client.websocket.send(User))
})
用户端
// websocket收到信息的处理逻辑
webSocket.onmessage = function (event) {
if (event.data) {
let res = JSON.parse(event.data)
console.log(res)
switch (res.type) {
// 当消息为用户列表
case 'users':
userList.innerHTML = res.onlineUser.map(val => {
if (val == uid) {
return '<li>me:' + val + '</li>'
} else {
return '<li>' + val + '</li>'
}
}).join('')
break
default:
}
}
}
3、基于ajax+websocket获取与某个用户的聊天信息
服务器端收到用户发送给另一个用户的消息后,遍历在线用户列表并匹配到接收消息的用户,将消息发送给所匹配到的用户。
ws.on('message', (msg) => {
// 将收到的消息转成对象格式
const buf = Buffer.from(msg)
let notice = buf.toString('utf-8')
let Notice = JSON.parse(notice)
// 通知当前用户消息发送成功
ws.send(JSON.stringify({
msg: Notice.msg,
type: 'chat'
}))
// 存储当前用户发送的数据
chatInfo.push({
from: uid,
to: Notice.receiver,
msg: Notice.msg
})
// 根据receiver匹配到接收消息的用户并发送通知
onlineUser.forEach(function each(client) {
if (client.id === Notice.receiver) {
if (client.websocket.readyState === 1) {
client.websocket.send(JSON.stringify({
msg: Notice.msg,
type: 'chat'
}));
}
}
});
})
用户端,当接收到消息或者信息发送成功时,刷新聊天界面,重新请求聊天信息。
// websocket收到信息的处理逻辑
webSocket.onmessage = function (event) {
if (event.data) {
let res = JSON.parse(event.data)
console.log(res)
switch (res.type) {
// 当消息为用户列表
case 'users':
userList.innerHTML = res.onlineUser.map(val => {
if (val == uid) {
return '<li>me:' + val + '</li>'
} else {
return '<li>' + val + '</li>'
}
}).join('')
break
// 当消息为聊天信息,刷新聊天框
case 'chat':
if (chatBoxId) {
reloadChatInfo()
}
break
default:
}
}
}
// 根据当前聊天用户重新获取聊天信息
function reloadChatInfo() {
getDataMethod('get', 'http://localhost:8191/getchat', {
friendId: chatBoxId,
id: uid
}).then(res => {
let chatlist = JSON.parse(res)
chatcontent.innerHTML = chatlist.map(val => {
if (val.from == uid) {
return '<p class="me">' + val.msg + ':me</p>'
} else {
return `<p class="other">${val.from}:${val.msg}</p>`
}
}).join('')
})
}
// 获取聊天信息的ajax请求
function getDataMethod(method, url, params) {
if (params) {
let arr = []
for (let key in params) {
arr.push(key + '=' + params[key])
}
url = url + '?' + arr.join('&')
}
return new Promise((resolve, reject) => {
let xml = new XMLHttpRequest()
xml.open(method, url, false)
xml.onreadystatechange = function () {
if (xml.readyState == 4){
console.log(xml)
if (xml.status == 200) {
resolve(xml.response)
} else {
reject(new Error(xml.statusText))
}
}
}
xml.send()
})
}
Node.JS服务器代码
// 导入 express
const express = require('express')
// 创建服务器的实例对象
const app = express()
const WebSocket = require('ws')
// 存储聊天信息
let chatInfo = []
// 存储在线用户
let onlineUser = []
const wss = new WebSocket.Server({ port: 5001 })
// 建立websocket连接
wss.on('connection', (ws, req, res) => {
console.log('server connection')
// 获取当前连接websocket的用户uid
let uid = req.url.substr(1)
let index = onlineUser.findIndex(val => val.id === uid)
if (index !== -1) {
onlineUser[index].websocket = ws
} else {
onlineUser.push({
websocket: ws,
id: uid
})
}
// 获取在线用户id数组
let User = JSON.stringify({
onlineUser: onlineUser.map(val => val.id),
type: 'users'
})
// 通知所有在线用户有新用户上线
onlineUser.forEach((client) => client.websocket.send(User))
ws.on('message', (msg) => {
// 将收到的消息转成对象格式
const buf = Buffer.from(msg)
let notice = buf.toString('utf-8')
let Notice = JSON.parse(notice)
// 通知当前用户消息发送成功
ws.send(JSON.stringify({
msg: Notice.msg,
type: 'chat'
}))
// 存储当前用户发送的数据
chatInfo.push({
from: uid,
to: Notice.receiver,
msg: Notice.msg
})
// 根据receiver匹配到接收消息的用户并发送通知
onlineUser.forEach(function each(client) {
if (client.id === Notice.receiver) {
if (client.websocket.readyState === 1) {
client.websocket.send(JSON.stringify({
msg: Notice.msg,
type: 'chat'
}));
}
}
});
})
// 连接关闭后清除在线用户
ws.on('close', res => {
let newUserArr = []
let newUserIds = []
for (let i = 0; i < onlineUser.length; i++) {
let val = onlineUser[i]
if (val.id !== uid) {
newUserArr.push(val)
newUserIds.push(val.id)
}
}
onlineUser = newUserArr
let User = JSON.stringify({
onlineUser: newUserIds,
type: 'users'
})
onlineUser.forEach((client) => client.websocket.send(User))
})
ws.send(JSON.stringify({
msg: '连接已建立',
type: 'system'
}))
})
//3.创建一个路由对象
let router = express.Router();
//4.设置路由
router.get('/getchat',(req,res)=>{
let fromid = req.query.id
let toid = req.query.friendId
// 返回当前用户与消息接收用户的聊天记录
let msgList = chatInfo.filter(val => (val.from === fromid && val.to === toid) || (val.from === toid && val.to === fromid))
res.send(msgList);
})
// 配置跨域请求中间件
const cors = require('cors')
app.use(cors())
app.use(router);
app.listen(8191, () => {
console.log('running')
})
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- <meta http-equiv="X-UA-Compatible" content="IE=edge"> -->
<meta http-equiv="Access-Control-Allow-Origin" content="*" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.content{
display: flex;
}
.leftbox{
width: 250px;
}
#rightbox{
width: 500px;
height: 500px;
background-color: black;
color: white;
}
#rightbox #chatcontent{
width: 100%;
height: 370px;
overflow: auto;
}
#rightbox #inputchat {
width: 100%;
height: 100px;
box-sizing: border-box;
}
#chatcontent .me{
text-align: right;
}
#chatcontent .other{
text-align: left;
}
#title{
height: 30px;
}
</style>
</head>
<body>
<button id="connectws">连接websocket</button>
<div class="content">
<div class="leftbox">
<p>在线用户:</p>
<ul id="userList">
</ul>
</div>
<div id="rightbox">
<div id="title"></div>
<div id="chatcontent">
</div>
<input type="text" name="" id="inputchat">
</div>
</div>
</body>
<script>
let lockReconnect = false // 是否允许重新连接
let timeout = 1000 * 20 // 心跳间隔
let reTimeout = 1000 * 10 // 重连间隔
let timeoutTimer = null // 心跳定时器
let reTimeoutTimer = null // 重连定时器
let webSocket = null
let uid = new Date().getTime()
let chatBoxId = ''
// 创建websocket连接
function createWebSocket () {
let
webSocket = new WebSocket('ws://localhost:5001/' + uid)
webSocket.binaryType = 'arraybuffer';
webSocket.onopen = function () {
console.log('连接成功...')
// 监听回车发送消息事件
window.onkeypress = function(e) {
if (e.keyCode === 13) {
let msg = inputchat.value
console.log(msg)
if (chatBoxId && msg !== '') {
webSocket.send(JSON.stringify({
receiver: chatBoxId,
msg: msg,
}))
inputchat.value = ''
}
}
}
}
webSocket.onerror = function () {
console.log('连接失败...')
reConnect()
}
// websocket收到信息的处理逻辑
webSocket.onmessage = function (event) {
if (event.data) {
let res = JSON.parse(event.data)
console.log(res)
switch (res.type) {
// 当消息为用户列表
case 'users':
userList.innerHTML = res.onlineUser.map(val => {
if (val == uid) {
return '<li>me:' + val + '</li>'
} else {
return '<li>' + val + '</li>'
}
}).join('')
break
// 当消息为聊天信息
case 'chat':
if (chatBoxId) {
reloadChatInfo()
}
break
default:
}
}
}
webSocket.onclose = function (event) {
console.log('连接已关闭...')
console.log('websocket 断开: ' + event.code + ' ' + event.reason + ' ' + event.wasClean)
reConnect()
}
}
// 重连
function reConnect () {
if (lockReconnect) {
return
}
lockReconnect = true
// 没连接上会一直重连,设置延迟避免请求过多
reTimeoutTimer && clearTimeout(reTimeoutTimer)
reTimeoutTimer = setTimeout(function () {
createWebSocket()
lockReconnect = false
}, reTimeout)
}
// 获取页面元素
let btnws = document.getElementById('connectws')
let title = document.getElementById('title')
let inputchat = document.getElementById('inputchat')
let chatcontent = document.getElementById('chatcontent')
let userList = document.getElementById('userList')
btnws.addEventListener('click', () => {
createWebSocket()
})
// 监听在线用户点击事件
userList.addEventListener('click', (e) => {
if (!e.target.innerText.includes('me')) {
chatBoxId = e.target.innerText
title.innerHTML = '与' + chatBoxId + '的聊天'
reloadChatInfo()
}
})
// 根据当前聊天用户重新获取聊天信息
function reloadChatInfo() {
getDataMethod('get', 'http://localhost:8191/getchat', {
friendId: chatBoxId,
id: uid
}).then(res => {
let chatlist = JSON.parse(res)
chatcontent.innerHTML = chatlist.map(val => {
if (val.from == uid) {
return '<p class="me">' + val.msg + ':me</p>'
} else {
return `<p class="other">${val.from}:${val.msg}</p>`
}
}).join('')
})
}
// 获取聊天信息的ajax请求
function getDataMethod(method, url, params) {
if (params) {
let arr = []
for (let key in params) {
arr.push(key + '=' + params[key])
}
url = url + '?' + arr.join('&')
}
return new Promise((resolve, reject) => {
let xml = new XMLHttpRequest()
xml.open(method, url, false)
xml.onreadystatechange = function () {
if (xml.readyState == 4){
console.log(xml)
if (xml.status == 200) {
resolve(xml.response)
} else {
reject(new Error(xml.statusText))
}
}
}
xml.send()
})
}
</script>
</html>