社群互動平台
WebSocket與Node.js打造即時社群互動平台教學
目錄
- 專案設置與依賴安裝
- 服務器端設置
- 客戶端實現
- 核心功能實現
- 進階功能與優化
1. 專案設置與依賴安裝
首先創建一個新的專案目錄並初始化:
mkdir websocket-social-platform
cd websocket-social-platform
npm init -y
安裝必要的依賴:
npm install express ws dotenv mongoose
npm install --save-dev nodemon
2. 服務器端設置
2.1 基本服務器設置
創建 server.js
:
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const path = require('path');
require('dotenv').config();
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
// 設置靜態文件目錄
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
// WebSocket連接處理
wss.on('connection', (ws) => {
console.log('新用戶連接');
ws.on('message', (message) => {
const data = JSON.parse(message);
broadcastMessage(data, ws);
});
ws.on('close', () => {
console.log('用戶斷開連接');
});
});
// 廣播消息到所有連接的客戶端
function broadcastMessage(data, sender) {
wss.clients.forEach((client) => {
if (client !== sender && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
});
}
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`服務器運行在端口 ${PORT}`);
});
2.2 數據庫設置
創建 models/message.js
:
const mongoose = require('mongoose');
const messageSchema = new mongoose.Schema({
content: {
type: String,
required: true
},
username: {
type: String,
required: true
},
timestamp: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Message', messageSchema);
3. 客戶端實現
3.1 HTML結構
在 public/index.html
中:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>即時社群互動平台</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div class="chat-container">
<div id="messageList" class="message-list"></div>
<div class="input-container">
<input type="text" id="messageInput" placeholder="輸入訊息...">
<button onclick="sendMessage()">發送</button>
</div>
</div>
</div>
<script src="client.js"></script>
</body>
</html>
3.2 客戶端JavaScript
在 public/client.js
中:
let ws;
const username = prompt('請輸入您的用戶名:') || '匿名用戶';
function connectWebSocket() {
ws = new WebSocket(`ws://${window.location.host}`);
ws.onopen = () => {
console.log('WebSocket連接已建立');
displaySystemMessage('已連接到聊天室');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
displayMessage(data);
};
ws.onclose = () => {
console.log('WebSocket連接已關閉');
displaySystemMessage('與聊天室的連接已斷開');
// 嘗試重新連接
setTimeout(connectWebSocket, 3000);
};
ws.onerror = (error) => {
console.error('WebSocket錯誤:', error);
displaySystemMessage('連接發生錯誤');
};
}
function sendMessage() {
const messageInput = document.getElementById('messageInput');
const content = messageInput.value.trim();
if (content && ws.readyState === WebSocket.OPEN) {
const message = {
type: 'chat',
content: content,
username: username,
timestamp: new Date().toISOString()
};
ws.send(JSON.stringify(message));
displayMessage({...message, isSelf: true});
messageInput.value = '';
}
}
function displayMessage(data) {
const messageList = document.getElementById('messageList');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${data.isSelf ? 'self' : 'other'}`;
const timestamp = new Date(data.timestamp).toLocaleTimeString();
messageDiv.innerHTML = `
<span class="username">${data.username}</span>
<span class="content">${data.content}</span>
<span class="timestamp">${timestamp}</span>
`;
messageList.appendChild(messageDiv);
messageList.scrollTop = messageList.scrollHeight;
}
function displaySystemMessage(message) {
const messageList = document.getElementById('messageList');
const messageDiv = document.createElement('div');
messageDiv.className = 'system-message';
messageDiv.textContent = message;
messageList.appendChild(messageDiv);
messageList.scrollTop = messageList.scrollHeight;
}
// 初始化WebSocket連接
connectWebSocket();
// 監聽Enter鍵發送消息
document.getElementById('messageInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
3.3 CSS樣式
在 public/style.css
中:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f0f2f5;
}
.container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
.chat-container {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.message-list {
height: 500px;
overflow-y: auto;
padding: 20px;
}
.message {
margin-bottom: 10px;
padding: 10px;
border-radius: 8px;
max-width: 70%;
}
.message.self {
background-color: #0084ff;
color: white;
margin-left: auto;
}
.message.other {
background-color: #e9ecef;
color: black;
}
.username {
font-weight: bold;
display: block;
margin-bottom: 5px;
}
.timestamp {
font-size: 0.8em;
color: #666;
margin-left: 10px;
}
.system-message {
text-align: center;
color: #666;
margin: 10px 0;
font-style: italic;
}
.input-container {
display: flex;
padding: 20px;
border-top: 1px solid #ddd;
}
input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
}
button {
padding: 10px 20px;
background-color: #0084ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0066cc;
}
4. 核心功能實現
4.1 用戶在線狀態管理
在服務器端添加用戶管理:
const connectedUsers = new Map();
wss.on('connection', (ws) => {
ws.id = generateUserId();
ws.on('message', (message) => {
const data = JSON.parse(message);
if (data.type === 'login') {
connectedUsers.set(ws.id, {
username: data.username,
ws: ws
});
broadcastUserList();
}
});
ws.on('close', () => {
connectedUsers.delete(ws.id);
broadcastUserList();
});
});
function broadcastUserList() {
const userList = Array.from(connectedUsers.values()).map(u => u.username);
const message = {
type: 'userList',
users: userList
};
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
}
});
}
4.2 消息持久化
添加數據庫連接和消息存儲:
const Message = require('./models/message');
const mongoose = require('mongoose');
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
async function saveMessage(data) {
try {
const message = new Message({
content: data.content,
username: data.username
});
await message.save();
} catch (error) {
console.error('保存消息失敗:', error);
}
}
// 在WebSocket消息處理中添加
ws.on('message', async (message) => {
const data = JSON.parse(message);
if (data.type === 'chat') {
await saveMessage(data);
}
broadcastMessage(data, ws);
});
5. 進階功能與優化
5.1 消息重傳機制
function sendMessageWithRetry(ws, data, maxRetries = 3) {
let retries = 0;
function trySend() {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
} else if (retries < maxRetries) {
retries++;
setTimeout(trySend, 1000);
}
}
trySend();
}
5.2 心跳檢測
function setupHeartbeat(ws) {
ws.isAlive = true;
ws.on('pong', () => {
ws.isAlive = true;
});
const interval = setInterval(() => {
if (ws.isAlive === false) {
ws.terminate();
return;
}
ws.isAlive = false;
ws.ping();
}, 30000);
ws.on('close', () => {
clearInterval(interval);
});
}
5.3 錯誤處理
process.on('unhandledRejection', (error) => {
console.error('未處理的Promise拒絕:', error);
});
process.on('uncaughtException', (error) => {
console.error('未捕獲的異常:', error);
// 記錄錯誤並優雅地關閉應用
process.exit(1);
});
部署說明
- 環境變數設置:
創建.env
文件:
PORT=3000
MONGODB_URI=mongodb://localhost:27017/chat
- 啟動應用:
# 開發環境
npm run dev
# 生產環境
npm start
總結
這個教學展示了如何使用WebSocket和Node.js構建一個基礎的社群互動平台。主要功能包括:
- 即時消息發送和接收
- 用戶在線狀態管理
- 消息持久化儲存
- 錯誤處理和重試機制
- 心跳檢測保持連接
您可以根據需求進一步擴展功能,例如添加私聊、群組聊天、檔案傳輸等功能。