node.js + html + Sealos容器云 搭建简易多人实时聊天室demo 带源码
node.js + html + Sealos容器云 搭建简易多人实时聊天室demo 带源码
- 前言
- 功能介绍(demo演示)
- sealos官网配置
- node.js 编写服务端代码
- 前端ui + 调用接口
- 整体项目目录
- 部署到服务器
前言
hello哦盆友们,这次我们来十几行代码做一个超简单的多人聊天室,涉及功能不多,只是让大家熟悉娱乐一下,这次我们一切从简 使用到的技术为 node.js + html + sealos 云储存 * (sealos官方没给我打钱有官网人员看到了记得给打点。)
功能介绍(demo演示)
这是本地启动的服务,实现多人实时聊天的demo
多人实时聊天
sealos官网配置
sealos 云储存
由于我们聊天需要储存聊天记录等数据,所以这次使用了sealos云里面的 mongoDB 来储存我们的聊天数据, sealos云注册免费赠送我们额度,足够我们使用了,下面介绍一下sealos如何使用。
✨ 首先进入 sealos 云 官网 没有账户的注册一个账户,完成后登录我们点击极速体验按钮进入到工作台
✨ 进入到工作台后我们在下面找到数据库 如下图
✨ 进入到工作台后我们右上角选择新建 然后选择mongoDB 直接
✨ 这样我们就创建好了一个云的mogoDB储存啦 途中方框圈中的地方呆会儿我们在node服务里面要使用
node.js 编写服务端代码
✨ 首先我们在一个文件夹内初始化一个package.json 文件,我们这次使用到了 express框架 + socket.io + cors + mongoose 大家安装这几个依赖就可以了 复制到同学别忘了 npm install 一下哦
{"name": "chat-server","version": "1.0.0","description": "实时聊天服务器","main": "server.js","scripts": {"start": "node server.js","dev": "nodemon server.js"},"dependencies": {"express": "^4.17.1","socket.io": "^4.4.1","cors": "^2.8.5","mongoose": "^6.8.0"},"devDependencies": {"nodemon": "^2.0.15"}
}
✨ 下面我们创建一个 server.js 功能作用我都给注释到代码层面了可自行研究
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http, {cors: {origin: '*', // 允许所有来源访问methods: ['GET', 'POST'],allowedHeaders: ['Content-Type'],credentials: true}
});
const cors = require('cors');
const mongoose = require('mongoose');const connectWithRetry = async () => {try {await mongoose.connect('这里替换成你的', {useNewUrlParser: true,useUnifiedTopology: true,serverSelectionTimeoutMS: 5000,socketTimeoutMS: 45000,});console.log('MongoDB 连接成功');} catch (err) {console.error('MongoDB 连接失败,5秒后重试:', err);setTimeout(connectWithRetry, 5000);}
};connectWithRetry();// 添加 MongoDB 连接错误处理
mongoose.connection.on('error', err => {console.error('MongoDB 连接错误:', err);
});mongoose.connection.on('disconnected', () => {console.log('MongoDB 连接断开');
});// 定义消息模型
const messageSchema = new mongoose.Schema({userId: String,username: String,content: String,timestamp: { type: Date, default: Date.now }
});const Message = mongoose.model('Message', messageSchema);// 启用 CORS
app.use(cors({origin: '*', // 允许所有来源访问,生产环境建议设置具体的域名methods: ['GET', 'POST'],allowedHeaders: ['Content-Type'],credentials: true
}));
app.use(express.json());// 存储在线用户
let onlineUsers = new Map();// Socket.IO 连接处理
io.on('connection', (socket) => {console.log('用户已连接');// 获取历史消息socket.on('getHistory', async () => {try {const messages = await Message.find().sort({ timestamp: -1 }).limit(50); // 限制返回最近50条消息socket.emit('history', messages.reverse());} catch (err) {console.error('获取历史消息失败:', err);}});// 用户加入聊天socket.on('join', (userData) => {onlineUsers.set(socket.id, userData.username);io.emit('userList', Array.from(onlineUsers.values()));});// 处理消息发送socket.on('sendMessage', async (message) => {const messageData = {userId: socket.id,username: onlineUsers.get(socket.id),content: message,timestamp: new Date()};try {// 保存消息到数据库const newMessage = new Message(messageData);await newMessage.save();// 广播消息给所有客户端io.emit('message', messageData);} catch (err) {console.error('保存消息失败:', err);socket.emit('error', '消息发送失败');}});// 处理断开连接socket.on('disconnect', () => {onlineUsers.delete(socket.id);io.emit('userList', Array.from(onlineUsers.values()));console.log('用户已断开连接');});
});// 添加获取历史消息的 REST API
app.get('/api/messages', async (req, res) => {try {const page = parseInt(req.query.page) || 1;const limit = parseInt(req.query.limit) || 50;const messages = await Message.find().sort({ timestamp: -1 }).skip((page - 1) * limit).limit(limit);const total = await Message.countDocuments();res.json({messages: messages.reverse(),pagination: {current: page,limit,total}});} catch (err) {res.status(500).json({ error: '获取消息失败' });}
});// 基础路由
app.get('/', (req, res) => {res.send('聊天服务器正在运行');
});// 启动服务器
const PORT = process.env.PORT || 3000;
http.listen(PORT, () => {console.log(`服务器运行在端口 ${PORT}`);
});
前端ui + 调用接口
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>实时聊天室</title><style>.chat-container {max-width: 800px;margin: 0 auto;padding: 20px;}.messages {height: 400px;border: 1px solid #ccc;overflow-y: auto;padding: 10px;margin-bottom: 20px;}.message {margin-bottom: 10px;padding: 5px;}.message .username {font-weight: bold;color: #2196F3;}.message .time {color: #999;font-size: 0.8em;}.input-area {display: flex;gap: 10px;}#messageInput {flex: 1;padding: 8px;}.user-list {border: 1px solid #ccc;padding: 10px;margin-bottom: 20px;}</style>
</head>
<body><div class="chat-container"><h2>实时聊天室</h2><div class="user-list"><h3>在线用户</h3><ul id="userList"></ul></div><div class="messages" id="messages"></div><div class="input-area"><input type="text" id="messageInput" placeholder="输入消息..."><button onclick="sendMessage()">发送</button></div></div><script src="https://cdn.socket.io/4.4.1/socket.io.min.js"></script><script>// 连接 Socket.IO 服务器const socket = io('http://localhost:3000');// 获取DOM元素const messagesDiv = document.getElementById('messages');const messageInput = document.getElementById('messageInput');const userList = document.getElementById('userList');// 生成随机用户名const username = '用户' + Math.floor(Math.random() * 1000);// 加入聊天socket.emit('join', { username });// 获取历史消息socket.emit('getHistory');// 监听历史消息socket.on('history', (messages) => {messages.forEach(message => {appendMessage(message);});scrollToBottom();});// 监听新消息socket.on('message', (message) => {console.log(message,"message");appendMessage(message);scrollToBottom();});// 监听用户列表更新socket.on('userList', (users) => {userList.innerHTML = users.map(user => `<li>${user}</li>`).join('');});// 发送消息function sendMessage() {const message = messageInput.value.trim();if (message) {socket.emit('sendMessage', message);messageInput.value = '';}}// 添加消息到界面function appendMessage(message) {const messageDiv = document.createElement('div');messageDiv.className = 'message';const time = new Date(message.timestamp).toLocaleTimeString();messageDiv.innerHTML = `<span class="username">${message.username}</span><span class="time">${time}</span><div class="content">${message.content}</div>`;messagesDiv.appendChild(messageDiv);}// 滚动到底部function scrollToBottom() {messagesDiv.scrollTop = messagesDiv.scrollHeight;}// 按回车发送消息messageInput.addEventListener('keypress', (e) => {if (e.key === 'Enter') {sendMessage();}});// 加载更多历史消息let currentPage = 1;async function loadMoreMessages() {try {const response = await fetch(`http://localhost:3000/api/messages?page=${currentPage}&limit=20`);const data = await response.json();data.messages.forEach(message => {const messageDiv = document.createElement('div');messageDiv.className = 'message';// ... 处理消息显示});currentPage++;} catch (error) {console.error('加载消息失败:', error);}}</script>
</body>
</html>
整体项目目录
部署到服务器
这里大家自行部署到服务器就行,给你的好友展示一下。如果有需要部署教程的 等有时间出个部署教程
相关文章:

node.js + html + Sealos容器云 搭建简易多人实时聊天室demo 带源码
node.js html Sealos容器云 搭建简易多人实时聊天室demo 带源码 前言功能介绍(demo演示)sealos官网配置node.js 编写服务端代码前端ui 调用接口整体项目目录部署到服务器 前言 hello哦盆友们,这次我们来十几行代码做一个超简单的多人聊天…...

OpenFeign远程调用返回的是List<T>类型的数据
在使用 OpenFeign 进行远程调用时,如果接口返回的是 List 类型的数据,可以通过以下方式处理: 直接定义返回类型为List Feign 默认支持 JSON 序列化/反序列化,如果服务端返回的是 List的JSON格式数据,可以直接在 Feig…...

PCL 计算多边形的面积【2025最新版】
目录 一、算法原理1、概述2、主要函数3、函数源码二、代码实现三、结果展示博客长期更新,本文最近更新时间为:2025年1月17日。 一、算法原理 1、概述 根据给定的多边形的点云计算多边形的面积 A r e a = 1 2 ∑...

著名大模型评测榜单(不同评测方式)
在评估大语言模型的性能时,一种主流的途径就是选择不同的能力维度并且构建对应的评测任务,进而使用这些能力维度的评测任务对模型的性能进行测试与对比。由大型机构或者研究院所排出榜单。 评测指标 不同评测任务有不同的评指标,衡量模型的…...

国内知名Deepseek培训师培训讲师唐兴通老师讲授AI人工智能大模型实践应用
课程名称 《Deepseek人工智能大模型实践应用》 课程目标 全面了解Deepseek人工智能大模型的技术原理、功能特点及应用场景。 熟练掌握Deepseek大模型的提示词工程技巧,能够编写高质量的提示词。 掌握Deepseek大模型在办公、营销等领域的应用方法,提升…...

【AIGC】冷启动数据与多阶段训练在 DeepSeek 中的作用
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AIGC | ChatGPT 文章目录 💯前言💯冷启动数据的作用冷启动数据设计 💯多阶段训练的作用阶段 1:冷启动微调阶段 2:推理导向强化学习(RL࿰…...

如何打造一个更友好的网站结构?
在SEO优化中,网站的结构往往被忽略,但它其实是决定谷歌爬虫抓取效率的关键因素之一。一个清晰、逻辑合理的网站结构,不仅能让用户更方便地找到他们需要的信息,还能提升搜索引擎的抓取效率 理想的网站结构应该像一棵树,…...
【ROS2】RViz2自定义面板插件(rviz_common::Panel)的详细步骤
【ROS】郭老二博文之:ROS目录 1、简述 RViz2 的插件基于 ROS2 的插件库(pluginlib)机制,通过动态加载共享库实现功能扩展。 注意:RViz2 使用 QT 作为 UI 框架,虽然 QT 也有插件机制,但是 RViz2 并没有使用QT的插件机制,而是通过 pluginlib 加载功能模块来实现。 2、…...

漏洞分析 Spring Framework路径遍历漏洞(CVE-2024-38816)
漏洞概述 VMware Spring Framework是美国威睿(VMware)公司的一套开源的Java、JavaEE应用程序框架。该框架可帮助开发人员构建高质量的应用。 近期,监测到Spring Framework在特定条件下,存在目录遍历漏洞(网宿评分&am…...
《手札·避坑篇》2025年传统制造业企业数字化转型指南
一、引言 在数字化浪潮的推动下,传统制造业企业正加速向智能化、数字化转型。开源软件技术与制造MES(制造执行系统)产品的结合,为企业提供了高效、灵活且低成本的转型路径。本指南旨在为传统制造业企业的信息化负责人提供一套完整的数字化转型方案,助力企业实现高效、智能…...
MySQL中DDL操作是否支持事务
MySQL中DDL不支持事务。 传统MySQL(5.7及以前版本): DDL操作不支持事务执行DDL操作时会隐式提交当前会话的事务无法回滚DDL操作 MySQL 8.0版本: 引入了原子DDL特性(Atomic DDL)DDL操作变为原子性的&…...

GWO优化决策树回归预测matlab
灰狼优化算法(Grey Wolf Optimizer,简称 GWO)是一种群智能优化算法,由澳大利亚格里菲斯大学的 Mirjalii 等人于 2014 年提出。该算法的设计灵感源自灰狼群体的捕食行为,核心思想是模仿灰狼社会的结构与行为模式。 在本…...

掌握Spring @SessionAttribute:跨请求数据共享的艺术
SessionAttribute注解在Spring中的作用,就像是一个“数据中转站”。 在Web应用中,我们经常需要在多个请求之间共享数据。比如,用户登录后,我们需要在多个页面或请求中保持用户的登录状态。这时,SessionAttribute注解就…...

python读取Excel表格内公式的值
背景:在做业务周报的时候,有一个Excel模板,表里面包含了一些公式,dataframe写入到Excel的时候,有公式的部分通过python读出来的结果是None,需要进行优化参考链接: 如何使用openpyxl读取Excel单元…...
第三十八章:阳江自驾之旅:挖蟹与品鲜
经历了惠州海边那趟温馨又欢乐的自驾之旅后,小冷和小颖心中对旅行的热情愈发高涨。闲暇时,两人总会坐在客厅里,翻看着旅行杂志,或是在网上搜索各地的美景,那些充满魅力的地方不断吸引着他们,也让他们对下一…...

C++小等于的所有奇数和=最大奇数除2加1的平方。
缘由 三种思路解题:依据算术推导得到一个规律:小等于的所有奇数和等于最大奇数除以2加1的平方。将在后续发布,总计有十种推导出来的实现代码。 int a 0,aa 1,aaa 0;cin >> a; while (aa<a) aaa aa, aa 2;cout << aaa;i…...

设置IDEA的内存大小,让IDEA更流畅: 建议设置在 2048 MB 及以上
文章目录 引言I 更改内存设置基于窗口界面进行内存设置修改内存配置文件II IDEA中的一些常见问题及其解决方案引言 方式一:基于窗口界面进行内存设置方式二:修改内存配置文件I 更改内存设置 基于窗口界面进行内存设置 打开IDEA,上方菜单栏 Help > Change Memory Settin…...
Ranger Hive Service连接测试失败问题解决
个人博客地址:Ranger Hive Service连接测试失败问题解决 | 一张假钞的真实世界 异常信息如下: org.apache.ranger.plugin.client.HadoopException: Unable to connect to Hive Thrift Server instance.. Unable to connect to Hive Thrift Server inst…...
车机音频参数下发流程
比如以audioControlWrapper.setParametersToAmp(keyPairValues); 下发banlance为例,链路如下 hal层 1. AudioControl.cpp hardware\interfaces\automotive\audiocontrol\aidl\default\AudioControl.cpp ndk::ScopedAStatus AudioControl::setParametersToAmp(co…...

大模型推理——MLA实现方案
1.整体流程 先上一张图来整体理解下MLA的计算过程 2.实现代码 import math import torch import torch.nn as nn# rms归一化 class RMSNorm(nn.Module):""""""def __init__(self, hidden_size, eps1e-6):super().__init__()self.weight nn.Pa…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...

FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...