ساخت چتآنلاین با Node.js و Socket.io
چتآنلاین (Realtime Chat) یکی از رایجترین کاربردهای برنامههای وب است. در این مقاله با استفاده از Node.js و Socket.io روش پیادهسازی چت آنلاین را از پایه توضیح میدهیم، الگوها و نکات عملی برای تولید، مقیاسپذیری و امنیت را مرور میکنیم و نمونههای کد کاربردی میآوریم.
چرا Node.js و Socket.io؟
- Node.js با مدل غیرهمزمان و تکنخی برای I/O سنگین مناسب است.
- Socket.io لایهای بالاتر از WebSocket ارائه میدهد: fallback برای مرورگرهای قدیمی، reconnection، و API ساده برای رویدادها.
- پروژههای چت نیاز به تأخیر کم، ارسال/دریافت بلادرنگ پیام و مدیریت کاربران دارند که Socket.io این امکانات را ساده میکند.
معماری کلی
معماری معمول شامل یک سرور Node.js با Socket.io، یک کلاینت جاوااسکریپت در مرورگر و در اکثر پیادهسازیها پایگاهداده برای نگهداری پیامهاست. برای مقیاسپذیری از Redis Adapter برای Pub/Sub بین چند نمونه سرور استفاده میشود.
نمونه ساده: سرور و کلاینت
// server.js (Node.js + Express + Socket.io)
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: { origin: '*' }
});
io.on('connection', (socket) => {
console.log('user connected', socket.id);
socket.on('joinRoom', (room) => {
socket.join(room);
socket.to(room).emit('message', { system: true, text: 'A user joined' });
});
socket.on('chatMessage', (msg) => {
// validate msg before broadcasting
const sanitized = String(msg.text || '').slice(0, 1000);
io.to(msg.room).emit('message', { user: msg.user, text: sanitized, time: Date.now() });
});
socket.on('disconnect', () => {
console.log('user disconnected', socket.id);
});
});
server.listen(3000, () => console.log('Server listening on 3000'));
این قطعه یک سرور ساده ایجاد میکند که روی اتصالها گوش میدهد، کاربر را به روم اضافه میکند و پیامها را به روم مربوطه پخش میکند. به نکات امنیتی مانند اعتبارسنجی و محدودیت طول پیام توجه شده است.
<!-- client.html -->
<!doctype html>
<div id="messages"></div>
<button id="send">Send</button> const socket = io('http://localhost:3000'); socket.emit('joinRoom', 'public'); socket.on('message', (m) => { const d = document.createElement('div'); d.textContent = (m.user ? m.user + ': ' : '') + m.text; document.getElementById('messages').appendChild(d); }); document.getElementById('send').onclick = () => { const txt = document.getElementById('input').value; socket.emit('chatMessage', { room: 'public', user: 'Alice', text: txt }); };
در کلاینت، Socket.io را از CDN بارگذاری کرده و به سرور متصل میشویم، به روم میپیوندیم و پیامها را ارسال/دریافت میکنیم.
اعتبارسنجی و احراز هویت
در پروژههای واقعی باید کاربران را احراز هویت و پیامها را بررسی کنید. استفاده از middleware در Socket.io امکانپذیر است.
// auth-middleware.js
io.use((socket, next) => {
const token = socket.handshake.auth?.token;
if (!token) return next(new Error('Authentication error'));
// verify token (e.g., JWT)
try {
const payload = verifyJwt(token);
socket.user = payload;
next();
} catch (err) {
next(new Error('Authentication error'));
}
});
این middleware توکن را از handshake دریافت و بررسی میکند، سپس اطلاعات کاربر را به شیء socket اضافه میکند تا در هندلرها در دسترس باشد.
مقیاسپذیری با Redis Adapter
برای اجرای چندین نمونه سرور باید پیامها بین نمونهها منتشر شود تا همه کلاینتها پیامها را دریافت کنند. Redis adapter کار را ساده میکند.
// clustering with redis
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.adapter(createAdapter(pubClient, subClient));
});
این کد دو کلاینت Redis میسازد و آنها را به عنوان adapter برای Socket.io تنظیم میکند تا پیامها بین سرورها منتشر شوند.
نکات امنیتی و بهینهسازی
- استفاده از HTTPS/WSS برای رمزنگاری ترافیک.
- محدودسازی اندازه و نرخ پیام (rate limiting) برای جلوگیری از سوءاستفاده.
- اسکریپتنویسی XSS: پیامها را قبل از نمایش در DOM پاکسازی کنید یا از textContent استفاده کنید.
- برای پیامهای حساس از سرور-تایماستمپ و sign استفاده کنید تا جعل دشوار شود.
- بررسی و مدیریت reconnection و backoff در کلاینت.
ذخیره پیام و طراحی دیتابیس
برای تاریخچه (chat history) معمولاً از دیتابیسهای NoSQL مثل MongoDB یا دیتابیسهای رابطهای با ایندکس مناسب استفاده میشود. انتخاب به بارخوانی و نیازهای جستجو بستگی دارد.
| نیاز | پیشنهاد |
|---|---|
| حجم بالا، خواندن سریع | MongoDB با شاردینگ و ایندکس timestamp |
| تراکنشها و روابط پیچیده | PostgreSQL با جداول پیام و رابطه کاربران |
الگوهای پیشرفته
- Namespaces: برای جداسازی سرویسها (مثلاً game vs support).
- Rooms: برای چت رومها یا گفتگوهای خصوصی.
- Event Acknowledgements: تضمین رسیدن پیام با callback از socket.io.
- Presence: نگهداری وضعیت آنلاین کاربران با TTL در Redis.
مثال: استفاده از acknowledgement
// on server
socket.on('privateMessage', (data, callback) => {
const ok = validate(data);
if (!ok) return callback({ status: 'error' });
io.to(data.toSocketId).emit('message', data);
callback({ status: 'ok', deliveredAt: Date.now() });
});
با مکانیزم acknowledgment کلاینت مطمئن میشود سرور پیام را پردازش کرده و میتواند بازخورد مناسب (مثلاً نشان Delivered) را نمایش دهد.
جمعبندی و نکات عملی
برای ساخت یک چتآنلاین حرفهای با Node.js و Socket.io:
- با یک پیادهسازی ساده شروع کنید و تستهای بار را انجام دهید.
- احراز هویت، اعتبارسنجی و محافظت در برابر حملات را از ابتدا طراحی کنید.
- برای مقیاسپذیری از Redis Adapter و load balancer پشتیبانیکننده از sticky-session یا استفاده از adapterهای cloud-native استفاده کنید.
- مانیتورینگ تاخیر، تعداد کانکشنها و مصرف منابع را راهاندازی کنید.
با رعایت این اصول میتوانید یک سیستم چت آنی قابل اعتماد، امن و مقیاسپذیر پیادهسازی کنید.
آیا این مطلب برای شما مفید بود ؟




