实现
<html lang="zh-CN"><head><meta charset="UTF-8"><title>Instant Messaging</title><!-- 引入Bootstrap CSS --><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"><style>body {font-family: Arial, sans-serif;display: flex;flex-direction: column;height: 100vh;}#chat {border: 1px solid #ccc;height: calc(100% - 120px);overflow-y: scroll;padding: 10px;}.message {margin-bottom: 10px;}.avatar {width: 50px;height: 50px;border-radius: 50%;}.message-content {background-color: #e1ffc7;padding: 10px;border-radius: 15px;max-width: 60%;word-wrap: break-word;}.message-content.other {background-color: #c7e1ff;}.message.self .message-content {margin-left: auto;}.message-time {font-size: 16px;}.message {display: flex;align-items: flex-start;margin-bottom: 10px;}.avatar {width: 50px;height: 50px;border-radius: 50%;margin-right: 10px;}.message-sender {font-size: 14px;margin: 0 0 5px 0;color: #999;}.message-content {background-color: #e1ffc7;padding: 10px;border-radius: 15px;max-width: 60%;word-wrap: break-word;display: flex;flex-direction: column;}.message-content.other {background-color: #c7e1ff;align-self: flex-start;}.self {display: flex;flex-direction: row-reverse;}#container-fluid {width: 88%;}/* #auth-buttons {width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;} *//* 确保body和html占满整个视口,并移除默认边距 */body,html {height: 100%;margin: 0;}/* 使用Flexbox布局使#auth-buttons居中 */#auth-buttons {position: absolute;display: flex;flex-direction: column;/* 垂直排列子元素 */align-items: center;/* 水平居中对齐 */justify-content: center;/* 垂直居中对齐 */width: 480px;top: 25%;left: 50%;margin-left: -240px;/* left: -50%; *//* 宽度设置为100% */}/* 可选:为输入框和按钮添加一些间距 */#wrapper {max-width: 400px;/* 设置最大宽度 */width: 100%;/* 宽度设置为100% */}.w-100 {width: 100px;}</style>
</head><body><!-- 登录注册表单 --><div id="auth-buttons"><div class="w-100 mb-4"><input id="account-input" class="form-control mb-2" type="text" placeholder="账户"><input id="password-input" class="form-control" type="password" placeholder="密码"></div><div class="w-100"><button id="login-btn" class="btn btn-primary mr-2 w-100 mb-2">登录</button><button id="register-btn" class="btn btn-secondary w-100">注册</button></div></div><div id="container-fluid" style="height: 80%;display: none;"><div class="row" style="height: 80%;"><div class="col" id="chat"></div></div><div>连接状态:<button type="button" class="btn btn-success btn-sm">Success</button></div><div class="col-auto" style="padding: 10px;"><input id="message" class="form-control" type="" placeholder="输入消息..." /><button id="sendBtn" class="btn btn-primary mt-2">发送</button></div></div><!-- 添加消息提示容器 --><div id="message-alert" class="alert alert-danger d-none" role="alert"><!-- 错误信息将插入到这里 --></div><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script><script>// 获取登录和注册按钮const loginBtn = document.getElementById('login-btn');const registerBtn = document.getElementById('register-btn');const authButtons = document.getElementById('auth-buttons');const chatContainer = document.getElementById('container-fluid');const accountInput = document.getElementById('account-input');const passwordInput = document.getElementById('password-input');const messageAlert = document.getElementById('message-alert');changeContentShow = () => {console.log(authButtons.style.display)authButtons.style.display = 'none';chatContainer.style.display = 'block';}// 登录按钮点击事件loginBtn.onclick = () => {const account = accountInput.value.trim();const password = passwordInput.value;if (account === '' || password === '') {// 使用Bootstrap的消息提示显示错误信息messageAlert.textContent = '账户和密码不能为空';messageAlert.classList.remove('d-none'); // 显示消息提示return;}messageAlert.style.display = 'none'ws.send(JSON.stringify({ type: 'login', username: account, password }));// 登录成功后隐藏登录按钮,显示聊天界面changeContentShow()};const setTimer = (time) => {return new Promise((resolve, reject) => {setTimeout(() => {resolve();}, time);});}// 注册按钮点击事件registerBtn.onclick = () => {// 执行注册逻辑const account = accountInput.value.trim();const password = passwordInput.value;if (account === '' || password === '') {// 使用Bootstrap的消息提示显示错误信息messageAlert.textContent = '账户和密码不能为空';messageAlert.classList.remove('d-none'); // 显示消息提示return;}messageAlert.style.display = 'none'ws.send(JSON.stringify({ type: 'register', username: account, password }));// ...// 注册成功后隐藏注册按钮,显示聊天界面changeContentShow()};// ...(保留原有的WebSocket连接和其他代码)...const ws = new WebSocket('ws://localhost:8082');const chat = document.getElementById('chat');const messageInput = document.getElementById('message');const sendBtn = document.getElementById('sendBtn');// 监听服务器发送的消息ws.onmessage = async (event) => {console.log(event.data);const data = JSON.parse(event.data);if (data.type === 'login' && data.code === 200) {localStorage.setItem('userInfo', JSON.stringify(data.clients));changeContentShow()return;}if (data.type === 'error') {alert(data.message);location.reload();} else {if (Array.isArray(data)) {data.forEach(m => {addMessage(m, true);});} else {addMessage(data, true);}}};// 发送消息sendBtn.onclick = () => {const msg = messageInput.value.trim();if (msg) {ws.send(JSON.stringify({ type: 'message', message: msg }));addMessage({ message: msg }, false); // 自己发送的消息messageInput.value = '';}};// 允许按 Enter 键发送消息messageInput.addEventListener('keyup', (event) => {if (event.key === 'Enter') {sendBtn.click();}});function addMessage(message, isOther, senderName = '') {const messageContainer = document.createElement('div');messageContainer.className = `message ${isOther ? 'other' : 'self'}`;// 添加头像const avatar = document.createElement('img');avatar.src = isOther ? './public/images/downloaded-image3.jpg' : './public/images/downloaded-image6.jpg'; // 替换为实际头像路径或默认头像console.log(localStorage.getItem('userInfo'))const username = JSON.parse(localStorage.getItem('userInfo'))?.username || '我';avatar.alt = senderName || (isOther ? message.sender : username);avatar.className = 'avatar';// 添加网名const name = document.createElement('div');name.className = 'message-sender';name.textContent = senderName || (isOther ? message.sender : username);// 添加消息内容const messageContent = document.createElement('div');if (isOther) {messageContent.className = `message-content other`;} else {messageContent.className = `message-content`;}messageContent.textContent = message.message;// 组合元素messageContainer.appendChild(avatar);messageContainer.appendChild(name);messageContainer.appendChild(messageContent);chat.appendChild(messageContainer);chat.scrollTop = chat.scrollHeight;}if (localStorage.getItem('userInfo')) {// const { username, password } = JSON.parse(localStorage.getItem('userInfo'));// ws.send(JSON.stringify({ type: 'login', username, password }));changeContentShow()}</script>
</body></html>
nodejs
const WebSocket = require('ws');
const fs = require('fs');
const path = require('path');const wss = new WebSocket.Server({ port: 8082 });
const usersFilePath = path.join(__dirname, 'users.html" title=json>json');
const chatLogFilePath = path.join(__dirname, 'chatlog.html" title=json>json');
let clientNameCounter = 1;
const clients = new Map();// 加载用户数据
let users = [];
if (fs.existsSync(usersFilePath)) {const data = fs.readFileSync(usersFilePath, 'utf-8');users = JSON.parse(data);
}// 当有客户端连接时触发
wss.on('connection', (ws) => {console.log('新客户端已连接');// 提示客户端输入用户名和密码// ws.send(JSON.stringify({ type: 'register' }));// 监听客户端的响应ws.on('message', (message) => {console.log(`收到消息: ${message}`);const data = JSON.parse(message) || {};if (data.type === 'register') {// 注册新用户if (users.find(user => user.username === data.username)) {ws.send(JSON.stringify({ type: 'error', message: '用户名已存在' }));} else {users.push({ username: data.username, password: data.password });fs.writeFileSync(usersFilePath, JSON.stringify(users, null, 2));const clientName = data.username;clients.set(ws, clientName);console.log(`客户端 ${clientName} 注册成功,连接数: `, wss.clients.size);}} else if (data.type === 'login') {// 验证用户const user = users.find(user => user.username === data.username && user.password === data.password);if (user) {const clientName = user.username;clients.set(ws, clientName);console.log(`客户端 ${clientName} 登录成功,连接数: `, wss.clients.size);// 读取并发送历史聊天记录给新连接的客户端let messages = [];if (fs.existsSync(chatLogFilePath)) {const data = fs.readFileSync(chatLogFilePath, 'utf-8');messages = JSON.parse(data);}ws.send(JSON.stringify({ type: 'login', code: 200, message: '登录成功', clients: user }));ws.send(JSON.stringify(messages));} else {ws.send(JSON.stringify({ type: 'error', message: '用户名或密码错误' }));}} else if (data.type === 'message') {// 处理客户端发送的消息// console.log(`收到消息: ${data.message}`);const msg = data.message;// console.log(clients)// 读取现有消息let messages = [];if (fs.existsSync(chatLogFilePath)) {const data = fs.readFileSync(chatLogFilePath, 'utf-8');messages = JSON.parse(data);}// 添加新消息messages.push({sender: clients.get(ws),message: msg,time: new Date().toISOString()});// 写回文件fs.writeFileSync(chatLogFilePath, JSON.stringify(messages, null, 2));// 广播消息给所有连接的客户端,除了发送者const msgJson = JSON.stringify({ sender: clients.get(ws), message: msg, time: new Date().toISOString() });console.log(`广播消息: ${msgJson}`);wss.clients.forEach((client) => {if (client !== ws && client.readyState === WebSocket.OPEN) {client.send(msgJson);}});}});// 处理客户端断开连接ws.on('close', () => {console.log(`客户端 ${clients.get(ws)} 已断开`);clients.delete(ws);});
});console.log('WebSocket服务器已启动,监听端口8082');