一、前言
上一篇文章介绍了websocket的详细用法与工具类的封装,本篇就基于websocket搭建一个简易实时的聊天室。
在本篇开始之前也可以去回顾一下websocket详细用法:WebSocket详解与封装工具类
二、基于node搭建后台websocket服务
首先确认本机电脑中是否安装node,可以通过cmd打开命令窗口运行node -v
查看node版本;
- 建议创建一个空项目:
npm init
- 一路回车就行,然后就得到了一个package.json的配置文件;安装
ws
模块
npm i ws
- 新建app.js文件,源码如下:
const WebSocket = require('ws');
// 随机姓名
const names = ['赵', '钱', '孙', '李', '周', '吴', '郑', '王', '冯', '陈', '褚', '卫', '蒋', '沈', '韩', '杨', '朱', '秦', '尤', '许', '何',
'吕', '施', '张', '孔', '曹', '严', '华', '金', '魏', '陶', '姜', '戚', '谢', '邹', '喻', '柏', '水', '窦', '章', '云', '苏', '潘', '葛',
'奚', '范', '彭', '郎', '鲁', '韦', '昌', '马', '苗', '凤', '花', '方', '俞', '任', '袁', '柳', '酆', '鲍', '史', '唐', '费', '廉', '岑',
'薛', '雷', '贺', '倪', '汤', '滕', '殷', '罗', '毕', '郝', '邬', '安', '常', '乐', '于', '时', '傅', '皮', '卞', '齐', '康', '伍', '余',
'元', '卜', '顾', '孟', '平', '黄', '和', '穆', '萧', '尹'
];
// 获取随便昵称
const getRandom = (min, max) => {
return Math.floor(Math.random() * (min - max) + max)
};
// 创建 WebSocket 服务器实例
const wss = new WebSocket.Server({
port: 8080
});
// 监听连接事件
wss.on('connection', function(socket) {
console.log('新连接');
const randomIndex = getRandom(0, names.length - 1);
socket.nickname = names.splice(randomIndex, 1)[0];
sendMessageToClient( '连接成功!进入聊天室!');
// 监听接收消息事件
socket.on('message', function(message) {
sendMessageToClient(message);
});
// 监听接收消息事件
socket.on('close', function() {
// 广播消息给所有客户端
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
nickname: socket.nickname,
msg: '退出聊天室!',
}));
}
});
});
// 监听接收消息事件
socket.on('error', function() {
// 广播消息给所有客户端
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
nickname: socket.nickname,
msg: '网络不佳,连接失败 !',
}));
}
});
});
// 发送消息到客户端
function sendMessageToClient(message) {
let targetMsg = message instanceof Buffer ? message.toString() : message;
// 广播消息给所有客户端
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
nickname: socket.nickname,
msg: targetMsg,
}));
}
});
}
});
- 最后启动node服务:
node app.js
;
三、前端构建聊天室
3.1 确定功能点
- 会话框中展示聊天内容;
- 输入框与发送按钮发送消息内容;
- 连接websocket,监听实时消息展示到界面中;
3.2 界面结构搭建
代码结构:
<div class="main_container">
<!-- 消息框 -->
<div id="msgList"></div>
<!-- 发送消息 -->
<div class="controls">
<input type="text" class="pushMsg" />
<button id="btn">发送</button>
</div>
</div>
3.3 代码逻辑
-
引入之前封装的websocket工具类;
<script src="./utils/websocket.js"></script>
-
实例化连接websocket;
const newWebSocket = new WebSocketClient('ws:localhost:8080'); newWebSocket.connect();
-
定义消息列表,监听返回消息信息,根据消息展示到界面中;
// 消息列表 const msgList = []; newWebSocket.addEventListener('message', handleMessage); // 处理返回数据 function handleMessage(msg) { const msgData = JSON.parse(msg.data || '{}'); console.log(msgData, "返回数据===="); if (msgData && typeof msgData.msg === 'string' && !msgData.msg.includes('ping')) { msgList.push({ name: msgData.nickname, msg: msgData.msg, }); const str = msgList.reduce((cur, next) => { let curMsg = ''; if (next.msg.includes('连接')) { curMsg = `<div class="center_msg">玩家:${next.name}_${next.msg}</div>`; } else { curMsg = `<div class="msg_line"> <div class="avatar">${next.name}</div> <div class="msg_text">${next.msg}</div> </div>`; } return cur += curMsg; }, ''); $('#msgList').html(str); // 获取设置了滚动属性的div标签 const div = document.getElementById('msgList'); // 设置滚动的顶点坐标为滚动的总高度 div.scrollTop = div.scrollHeight; } }newWebSocket.addEventListener('message', handleMessage); // 处理返回数据 function handleMessage(msg) { const msgData = JSON.parse(msg.data || '{}'); console.log(msgData, "返回数据===="); if (msgData && typeof msgData.msg === 'string' && !msgData.msg.includes('ping')) { msgList.push({ name: msgData.nickname, msg: msgData.msg, }); const str = msgList.reduce((cur, next) => { let curMsg = ''; if (next.msg.includes('连接')) { curMsg = `<div class="center_msg">玩家:${next.name}_${next.msg}</div>`; } else { curMsg = `<div class="msg_line"> <div class="avatar">${next.name}</div> <div class="msg_text">${next.msg}</div> </div>`; } return cur += curMsg; }, ''); $('#msgList').html(str); // 获取设置了滚动属性的div标签 const div = document.getElementById('msgList'); // 设置滚动的顶点坐标为滚动的总高度 div.scrollTop = div.scrollHeight; } }
-
监听输入框回车事件,与发送按钮点击事件,发送消息;
// 监听发送按钮 $('#btn').on('click', sendMessage); // 输入框回车事件 $('.pushMsg').keydown(function(event) { if (event.key === 'Enter') { sendMessage(); } }); // 发送消息 function sendMessage() { const msg = $('.pushMsg').val(); if (msg) { newWebSocket.send(`${msg}`); $('.pushMsg').val(''); } }
四、涉及小知识点
-
取消滚动条的展示,发送消息后默认展示最下方数据;
#msgList::-webkit-scrollbar { width: 0; }
// 获取设置了滚动属性的div标签 const div = document.getElementById('msgList'); // 设置滚动的顶点坐标为滚动的总高度 div.scrollTop = div.scrollHeight;
-
输入框监听keydown事件,当key为
Enter
时发送消息(也可设置其他按键)// 输入框回车事件 $('.pushMsg').keydown(function(event) { if (event.key === 'Enter') { sendMessage(); } });
-
取消input的光标进入的表框效果
outline: none;
-
鼠标小手的出现
cursor: pointer;
五、聊天室完整前端源码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>聊天通信</title>
<script src="./utils/jquery.min.js"></script>
<script src="./utils/websocket.js"></script>
<style>
.main_container {
width: 500px;
margin: auto;
}
#msgList {
width: 500px;
height: 400px;
overflow: auto;
border: 2px solid #8491fe;
border-radius: 10px;
padding: 6px;
background-color: #ebffff;
box-sizing: border-box;
}
#msgList::-webkit-scrollbar {
width: 0;
}
.msg_line {
margin: 10px 0;
display: flex;
flex-wrap: wrap;
}
.avatar {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #77ffff;
text-align: center;
line-height: 30px;
color: #333;
font-size: 12px;
margin-right: 6px;
}
.msg_text {
display: inline-block;
padding: 4px 12px;
border-radius: 6px;
background-color: #f0f1dd;
font-size: 14px;
}
.center_msg {
text-align: center;
color: #f19597;
}
.controls {
width: 100%;
display: flex;
justify-content: space-between;
margin-top: 10px;
}
input {
flex: 1;
outline: none;
height: 34px;
line-height: 34px;
border: 1px solid #d8d8d8;
border-radius: 4px;
padding: 0 4px;
color: #333;
margin-right: 12px;
}
#btn {
width: 80px;
height: 34px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="main_container">
<!-- 消息框 -->
<div id="msgList"></div>
<!-- 发送消息 -->
<div class="controls">
<input type="text" class="pushMsg" />
<button id="btn">发送</button>
</div>
</div>
<script src="./utils/websocket.js"></script>
<script>
// 消息列表
const msgList = [];
// 初始化websocket
const newWebSocket = new WebSocketClient('ws:localhost:8080');
newWebSocket.connect();
window.NewWebSocket = newWebSocket;
newWebSocket.addEventListener('message', handleMessage);
// 处理返回数据
function handleMessage(msg) {
const msgData = JSON.parse(msg.data || '{}');
console.log(msgData, "返回数据====");
if (msgData && typeof msgData.msg === 'string' && !msgData.msg.includes('ping')) {
msgList.push({
name: msgData.nickname,
msg: msgData.msg,
});
const str = msgList.reduce((cur, next) => {
let curMsg = '';
if (next.msg.includes('连接')) {
curMsg = `<div class="center_msg">玩家:${next.name}_${next.msg}</div>`;
} else {
curMsg = `<div class="msg_line">
<div class="avatar">${next.name}</div>
<div class="msg_text">${next.msg}</div>
</div>`;
}
return cur += curMsg;
}, '');
$('#msgList').html(str);
// 获取设置了滚动属性的div标签
const div = document.getElementById('msgList');
// 设置滚动的顶点坐标为滚动的总高度
div.scrollTop = div.scrollHeight;
}
}
// 监听发送按钮
$('#btn').on('click', sendMessage);
// 输入框回车事件
$('.pushMsg').keydown(function(event) {
if (event.key === 'Enter') {
sendMessage();
}
});
// 发送消息
function sendMessage() {
const msg = $('.pushMsg').val();
if (msg) {
newWebSocket.send(`${msg}`);
$('.pushMsg').val('');
}
}
</script>
</body>
</html>