基于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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/679823.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv8改进 | Conv篇 | 利用YOLOv10提出的C2fUIB魔改YOLOv8(附代码 + 完整修改教程)

一、本文介绍 本文给大家带来的改进机制是利用YOLOv10提出的C2fUIB模块助力YOLOv8进行有效涨点&#xff0c;其中C2fUIB模块所用到的CIB模块是一种紧凑的倒置块结构&#xff0c;它采用廉价的深度卷积进行空间混合&#xff0c;并采用成本效益高的点卷积进行通道混合。本文针对该…

Window10磁盘的分盘和合并

注意&#xff1a; 当我们c盘不够大需要扩大磁盘空间时&#xff0c;当c盘后面没有未划分的磁盘时候&#xff0c;我们是无法进行扩充c盘的&#xff0c;此时&#xff0c;我们可以先删除后面一个磁盘&#xff0c;再进行扩大。 如下&#xff1a;c盘后没有未分配的空间&#xff0c;…

Flink端到端的精确一次(Exactly-Once)

目录 状态一致性 端到端的状态一致性 端到端精确一次&#xff08;End-To-End Exactly-Once&#xff09; Flink内部的Exactly-Once 输入端保证 输出端保证 幂等写入 事务写入 Flink和Kafka连接时的精确一次保证 整体介绍 需要的配置 案例 状态一致性 流式计算本身就…

UI 自动化分布式测试 -Docker Selenium Grid

分布式测试Selenium Grid 对于大型项目或者有大量测试用例的项目,单机的测试环境往往无法快速完成所有测试用例的执行,此时自动化测试执行效率将会成为最大的瓶颈,Selenium Grid 可以通过多机的分布式架构允许测试用例并行运行,大大缩短了测试时间。 Selenium Grid 提供了多…

命令模式(行为型)

目录 一、前言 二、命令模式 三、总结 一、前言 命令模式&#xff08;Command Pattern&#xff09;是一种行为型设计模式&#xff0c;命令模式将一个请求封装为一个对象&#xff0c;从而可以用不同的请求对客户进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以…

此表单不安全,因此系统已关闭自动填充功能

问题截图&#xff1a; 截图就不放了&#xff0c;公司的系统不方便&#xff0c;就是form表单会有个提示“此表单不安全&#xff0c;因此系统已关闭自动填充功能” 解决思路&#xff1a; 1、问题原因 使用https访问&#xff0c;但表单提交地址是http的 2、查看表单配置 表单…

行业分析---造车新势力之理想汽车

1 前言 在之前的博客中&#xff0c;笔者撰写了多篇行业类分析的文章&#xff08;科技新能源&#xff09;&#xff1a; 《行业分析---我眼中的Apple Inc.》 《行业分析---马斯克的Tesla》 《行业分析---造车新势力之蔚来汽车》 《行业分析---造车新势力之小鹏汽车》 此类文章的受…

IIC信号质量测试、时序测试详解

IIC 时序图 信号质量测试 1、vIL: 低输入电平。 2、vIH: 高输入电平。 3、vhys: 施密特触发器输入的滞后。 4、vOL1: VDD>2V时&#xff0c;低电平输出电压&#xff08;漏极开路或集电极开路&#xff09;。 5、vOL3: VDD<2V时&#xff0c;低电平输出电压&#xff08;漏极开…

js 数字精确度

事情的起源&#xff1a; 项目中 填写的赔付金额是小数 传给后端需要 *100 9.87 *100 传给后端后是986.9999999999999 后端直接取整 就变成了9.86了 0.1 0.2 ! 0.3 console.log(0.1 0.2) //0.30000000000000004 console.log(0.1 0.2 0.3) //false1. 数字的存储 浮点数是用…

【因果推断python】14_控制混淆因素3

目录 不良控制 - 选择偏差 糟糕的 COP 关键思想 不良控制 - 选择偏差 让我们回到债务催收电子邮件的案例。 请记住&#xff0c;电子邮件是随机分配给客户的。 我们已经解释了什么是 信用额度 和 风险分 。 现在&#xff0c;让我们看看剩下的变量。 打开 是客户是否打开电子邮…

工厂模式——工厂方法模式+注册表

工厂方法模式的瑕疵 在前一篇笔记中我们介绍了工厂方法模式&#xff0c;示例的类图如下&#xff1a; 考虑一种情况&#xff1a;现在要在程序运行时&#xff0c;根据外部资源&#xff0c;动态的实例化对象。也就是说在编译期我们无法知道要实例化的对象的类型。因此在实例化的过…

el-input实现后缀图标和clearable的兼容,调整el-input clearable与自定义图标展示位置问题

背景&#xff1a;常见的输入框存在两个图标的展示效果都是清空在前搜索或其他图标在后 常见以及最终实现效果&#xff08;清空图标在前&#xff0c;搜索图标在后&#xff09; BUG以及el-input默认效果 问题排查 通过控制台审查元素能够发现&#xff0c;默认的效果是自定义图标…

数据结构_手撕七大排序(快排,归并,堆排,希尔,选择,插入,冒泡)

✨✨所属专栏&#xff1a;数据结构✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序…

使用wheelnav.js构建酷炫的动态导航菜单

目录 前言 一、WheelNav是什么 1、项目地址 2、关于开源协议 3、相关目录介绍 二、如何使用wheelnav.js 1、新建html页面 2、设置style样式 3、创建展示元素实现动态导航 三、参数即方法介绍 1、参数列表 2、运行方法 3、实际成果 四、总结 前言 用户体验永远是一…

『 Linux 』目录与软硬链接 (万字详解)

文章目录 如何理解目录目录项 目录中的权限问题根目录Dentry缓存文件的增删改查与文件系统关系软硬链接软链接硬链接 如何理解目录 目录是一个文件存在其对应独立的Inode; $ stat dirFile: ‘dir’Size: 4096 Blocks: 8 IO Block: 4096 directory Device: f…

栈的最小值

题目链接 栈的最小值 题目描述 注意点 执行push、pop和min操作的时间复杂度必须为O(1) 解答思路 使用两个栈&#xff0c;一个栈deque存储栈中对应的元素值&#xff0c;另一个栈minDeque存储当前栈中所有元素的最小值&#xff0c;当执行push(int x)操作&#xff0c;deque直…

【乐吾乐3D可视化组态编辑器】数据接入

数据接入 本文为您介绍3D数据接入功能&#xff0c;数据接入功能分为三个步骤&#xff1a;数据订阅、数据集管理、数据绑定 编辑器地址&#xff1a;3D可视化组态 - 乐吾乐Le5le 数据订阅 乐吾乐3D组态数据管理功能由次顶部工具栏中按钮数据管理打开。 在新弹窗中选择数据订阅…

白银票据~

一. 白银票据的原理 白银票据就伪造ST票据&#xff0c; kerberoasting是破解ST票据中的服务用户hash值&#xff0c;有以下区别&#xff1a; 白银票据&#xff1a;伪造的ST使用的是机器用户的Hash值 Kerberoasting:破解的是ST的域用户的hash值二. 白银票据的利用条件 1.域名 …

基于线性回归根据饮食习惯和身体状况估计肥胖水平

目录 1. 作者介绍2&#xff0e;饮食习惯与身体状况数据集介绍3&#xff0e;实验步骤3.1 数据分析3.2 可视化处理数据3.3 导入线性回归模型进行训练3.4 预测结果3.5 完整代码3.5.1 数据分析3.5.2 模型评估 参考文献 1. 作者介绍 刘欢&#xff0c;女&#xff0c;西安工程大学电子…

MySQL经典练习50题(上)(解析版)

所有笔记、生活分享首发于个人博客 想要获得最佳的阅读体验&#xff08;无广告且清爽&#xff09;&#xff0c;请访问本篇笔记 MySQL经典练习50题&#xff08;上&#xff09; 创建数据库和表 -- 建 表 -- 学 生 表 CREATE TABLE Student( s_id VARCHAR(20), s_name VARCHAR(2…