node.js + html + Sealos容器云 搭建简易多人实时聊天室demo 带源码

news/2025/2/9 5:51:31 标签: node.js, html, 前端, javascript, html5, vue

html" title=node.js>node.js + html + Sealos容器云 搭建简易多人实时聊天室demo 带源码

  • 前言
    • 功能介绍(demo演示)
    • sealos官网配置
    • html" title=node.js>node.js 编写服务端代码
    • 前端ui + 调用接口
    • 整体项目目录
    • 部署到服务器

前言

hello哦盆友们,这次我们来十几行代码做一个超简单的多人聊天室,涉及功能不多,只是让大家熟悉娱乐一下,这次我们一切从简 使用到的技术为 html" title=node.js>node.js + html + sealos 云储存 * (sealos官方没给我打钱有官网人员看到了记得给打点。)

功能介绍(demo演示)

这是本地启动的服务,实现多人实时聊天的demo

多人实时聊天

在这里插入图片描述

sealos官网配置

sealos 云储存

由于我们聊天需要储存聊天记录等数据,所以这次使用了sealos云里面的 mongoDB 来储存我们的聊天数据, sealos云注册免费赠送我们额度,足够我们使用了,下面介绍一下sealos如何使用。

✨ 首先进入 sealos 云 官网 没有账户的注册一个账户,完成后登录我们点击极速体验按钮进入到工作台
✨ 进入到工作台后我们在下面找到数据库 如下图
在这里插入图片描述
✨ 进入到工作台后我们右上角选择新建 然后选择mongoDB 直接
在这里插入图片描述
在这里插入图片描述
✨ 这样我们就创建好了一个云的mogoDB储存啦 途中方框圈中的地方呆会儿我们在node服务里面要使用
在这里插入图片描述

html" title=node.js>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> 

整体项目目录

在这里插入图片描述

部署到服务器

这里大家自行部署到服务器就行,给你的好友展示一下。如果有需要部署教程的 等有时间出个部署教程


http://www.niftyadmin.cn/n/5845622.html

相关文章

zzcms接口index.php id参数存在SQL注入漏洞

zzcms接口index.php id参数存在SQL注入漏洞 漏洞描述 ZZCMS 2023中发现了一个严重漏洞。该漏洞影响了文件/index.php中的某些未知功能,操纵参数id会导致SQL注入,攻击可能是远程发起的,该漏洞已被公开披露并可被利用。攻击者可通过sql盲注等手段,获取数据库信息。 威胁等级:…

AWK系统学习指南:从文本处理到数据分析的终极武器 介绍

目录 一、AWK核心设计哲学解析 1.1 记录与字段的原子模型 1.2 模式-动作范式 二、AWK编程语言深度解析 2.1 控制结构 说明&#xff1a; 2.2 关联数组 代码说明&#xff1a; 示例输入和输出&#xff1a; 注意事项&#xff1a; 2.3 内置函数库 三、高级应用技巧 3.1…

Delphi语言的云计算

Delphi语言的云计算应用探索 引言 随着信息技术的迅猛发展&#xff0c;云计算已经成为现代计算机科学中一个不可或缺的重要组成部分。云计算不仅改变了企业的IT基础设施部署方式&#xff0c;还开启了新一轮的经济发展模式。开发者们也在积极寻找合适的编程语言&#xff0c;以…

Pycharm 2024版本出现 We could not validate your license怎么办?解决方法一步到位!

问题&#xff1a;最开始认证会显示成功&#xff0c;但后续会不断弹出窗口显示无法认证 证书 解决&#xff1a; 保留你当前的破姐脚本&#xff0c;删除其他机活文件夹即可 1.先选择试用&#xff0c;进入项目创建界面&#xff0c;新建项目 new project![](https://i-blog.csdnim…

算法14(力扣622)设置循环队列

1、问题 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普…

优惠券平台(十五):实现兑换/秒杀优惠券功能(2)

业务背景 在上一节中&#xff0c;我们介绍了通过数据库扣减完成用户兑换优惠券的逻辑&#xff0c;这种方式虽然稳妥&#xff0c;但性能有所不足&#xff0c;因为主流程的操作是同步执行的&#xff0c;导致响应时间变长&#xff0c;吞吐量下降。在本章节中&#xff0c;我们通过…

基于对比增强的超声视频的域知识为乳腺癌诊断提供了深度学习

Domain Knowledge Powered Deep Learning for Breast Cancer Diagnosis Based on Contrast-Enhanced Ultrasound Videos 期刊分析摘要引言相关工作乳腺癌中的CAD基于乳房CEU的CAD方法整体框架原始C3D骨干领域知识指导的时间注意模块(DKG-TMA)域知识引导的通道注意模块数据集和实…

python 包和模块的导入机制详解!

油管看到一个非常好的视频&#xff0c;在这里对一些视频内的重点内容进行总结。 注&#xff1a;本文主要供自己复习使用&#xff0c;仅提供个人认为的重点内容&#xff0c;难免有不周到之处&#xff0c;如果想要详细地了解相关机制&#xff0c;请在油管搜索&#xff1a; Pytho…