首先说明,以下代码都是基于 Nodejs+webSocket搭建多人聊天室实现,我只是复现了一遍,画了张图供自己理解。
数据格式及大致过程简图:
1. 服务端 app.js 代码
const ws = require('nodejs-websocket')
const broadcast = (str) => {
server.connections.forEach((connect) => {
connect.sendText(JSON.stringify(str))
})
}
const getAllChatter = ()=>{
let chartterArr = [];
server.connections.forEach((connect)=>{
console.log(connect.nickname)
chartterArr.push({name:connect.nickname})
});
return chartterArr;
};
const server = ws.createServer((connect) => {
connect.on('text', (str) => {
let data = JSON.parse(str)
console.log(data);
switch (data.type) {
case 'setName':
connect.nickname = data.nickname
// 广播XXX进入房间;
broadcast({
type: 'serverInformation',
message: data.nickname + '进入房间'
})
// 广播更新在线用户列表
broadcast({
type: 'chatterList',
list: getAllChatter()
})
break;
case 'chat':
// 广播这条信息;
broadcast({
type: 'chat',
name: connect.nickname,
message: data.message
})
break;
default: break;
}
})
connect.on('close', () => {
broadcast({
type: 'serverInformation',
message: connect.nickname + '离开房间'
})
broadcast({
type: 'chatterList',
list: getAllChatter()
})
})
connect.on('error', (err) => {
console.log(err)
})
}).listen(3000, () => {
console.log('server running')
})
2. 客户端 app.html 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>多人聊天室</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<div class="all">
<div class = 'person'><text id="onLine">在线人数</text></div>
<div class="contain">
<div class="content" id="content"></div>
</div>
<div class="footer">
<input placeholder="设置用户名." name="userName" id="userName"/>
<button id="setName">设置</button>
<textarea placeholder="输入您要发送的消息" id="message" name="message"></textarea>
<button id="sendMessage">发送</button>
</div>
</div>
<div class = "all2">
<div class="title"><text>在线用户列表:</text></div>
<div id="userList" class="userList">
</div>
</div>
<script type="text/javascript" language="JavaScript">
//定义全局的变量
let ws = null;
//封装获取时间的函数
Date.prototype.Format = function (fmt) {
//author: xjj
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
};
// 封装:接收后台消息后创建div
const createDivWithData = (data) => {
let outer = document.createElement('div')
let div_time = document.createElement('p')
let div_msg = document.createElement('p')
switch (data.type) {
case 'serverInformation':
div_time.innerHTML = new Date().Format('yyyy-MM-dd hh:mm:ss')
div_msg.innerHTML = data.message;
break;
case 'chat':
div_time.innerHTML = new Date().Format('yyyy-MM-dd hh:mm:ss')
div_msg.innerHTML = data.name + ':' + data.message;
break;
}
outer.appendChild(div_time)
outer.appendChild(div_msg)
return outer
}
// 封装:向后台发送消息
const sendMsgToServer = () => {
let message = document.getElementById('message')
if (!message.value) return
let data = {
type: 'chat',
message: message.value
}
ws.send(JSON.stringify(data))
message.value = ''
}
//封装:向后台设置用户名
const sendNicknameToServer = () => {
let setName = document.getElementById('setName')
setName.onclick = () => {
let nickName = document.getElementById('userName')
let data = {
type: 'setName',
nickname: nickName.value
}
if (nickName.value){
ws.send(JSON.stringify(data))
}
setName.setAttribute('disabled', true);
setName.style.display = 'none'
nickName.setAttribute('disabled', true)
}
}
ws = new WebSocket('ws://127.0.0.1:3000')
ws.onopen = () => {
sendNicknameToServer()
}
document.getElementById('sendMessage').onclick = sendMsgToServer
ws.onmessage = (e) => {
let data = JSON.parse(e.data)
if (data.type == 'chatterList') {
let list = data.list
let length = list.length
let chatterList = document.getElementById('userList')
document.getElementById('onLine').innerText = `在线人数:${length}人`
for(let i = 0; i < length; i++) {
let chatterDiv = document.createElement('p')
chatterDiv.innerText = list[i].name
chatterList.appendChild(chatterDiv)
}
} else {
let con = document.getElementById('content')
con.appendChild(createDivWithData(data))
}
}
</script>
</body>
</html>
3. 样式代码 app.css
.all{
width: 400px;
height: 500px;
border: 1px dotted grey;
float: left;
margin-left: 20px;
}
.person {
width: 400px;
height: 50px;
line-height: 50px;
text-align: center;
}
.contain {
width: 400px;
height: 350px;
}
#content{
width: 99%;
height: 100%;
overflow-y: auto;
}
.footer {
width: 100%;
height: 100px;
border: 1px solid green;
display: flex;
flex-direction: row;
align-items: center;
}
.footer input{
width: 70px;
height: 50px;
float: left;
margin-right: 5px;
margin-left: 5px;
border-radius: 5%;
}
.footer button{
width: 50px;
height: 50px;
float: left;
background-color: deepskyblue;
border-radius: 10%;
margin-right: 5px;
}
.footer textarea{
width: 200px;
height: 80px;
overflow-y: scroll;
float: left;
margin-right: 5px;
border-radius: 5%;
}
/*在线用户的列表*/
.all2{
width: 150px;
height: 500px;
border:1px dotted green;
float: left;
}
.title{
width: 150px;
height:50px;
line-height: 50px;
background-color: bisque;
}
.userList{
overflow-y: scroll;
height: 450px;
}
4. nodemon 辅助开发
安装:npm i nodemon -g --save
启动:nodemon app
避免每次修改服务器代码都要手动重启服务器