基于websocket与node搭建简易聊天室

一、前言

上一篇文章介绍了websocket的详细用法与工具类的封装,本篇就基于websocket搭建一个简易实时的聊天室。

在本篇开始之前也可以去回顾一下websocket详细用法:WebSocket详解与封装工具类

在这里插入图片描述

二、基于node搭建后台websocket服务

首先确认本机电脑中是否安装node,可以通过cmd打开命令窗口运行node -v查看node版本;

  1. 建议创建一个空项目:
npm init
  1. 一路回车就行,然后就得到了一个package.json的配置文件;安装ws模块
npm i ws
  1. 新建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,
				}));
			}
		});
	}
});

  1. 最后启动node服务:node app.js;

三、前端构建聊天室

3.1 确定功能点

  1. 会话框中展示聊天内容;
  2. 输入框与发送按钮发送消息内容;
  3. 连接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 代码逻辑

  1. 引入之前封装的websocket工具类;

    <script src="./utils/websocket.js"></script>
    
  2. 实例化连接websocket;

    const newWebSocket = new WebSocketClient('ws:localhost:8080');
    newWebSocket.connect();
    
  3. 定义消息列表,监听返回消息信息,根据消息展示到界面中;

    // 消息列表
    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;
    	}
    }
    
  4. 监听输入框回车事件,与发送按钮点击事件,发送消息;

    // 监听发送按钮
    $('#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('');
    	}
    }
    

四、涉及小知识点

  1. 取消滚动条的展示,发送消息后默认展示最下方数据;

    #msgList::-webkit-scrollbar {
    		width: 0;
    }
    
    // 获取设置了滚动属性的div标签
    const div = document.getElementById('msgList');
    // 设置滚动的顶点坐标为滚动的总高度
    div.scrollTop = div.scrollHeight;
    
  2. 输入框监听keydown事件,当key为Enter时发送消息(也可设置其他按键)

    // 输入框回车事件
    $('.pushMsg').keydown(function(event) {
    	if (event.key === 'Enter') {
    		sendMessage();
    	}
    });
    
  3. 取消input的光标进入的表框效果

    outline: none;
    
  4. 鼠标小手的出现

    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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值