استفاده از WebSocket در Node.js
وبسوکت (WebSocket) یک پروتکل برای ارتباط دوطرفه و همزمان بین کلاینت و سرور است. در اکوسیستم Node.js، استفاده از WebSocket برای ساخت اپلیکیشنهای Real-time مانند چت، بازی تحت شبکه، داشبوردهای زنده و نوتیفیکیشن بسیار رایج است. در این مقاله به مفاهیم، مثالهای عملی، نکات امنیتی و روشهای مقیاسپذیری میپردازیم.
چرا WebSocket و چه زمانی از آن استفاده کنیم؟
- ارتباط دوطرفه (full-duplex) با تاخیر کم.
- کاهش سربار نسبت به polling یا long-polling.
- مناسب برای برنامههای Real-time مانند بازیها، چت و نمایش لحظهای داده.
کتابخانههای معروف در Node.js
| کتابخانه | ویژگیها |
|---|---|
| ws | سبک، عملکرد بالا، API ساده برای WebSocket خام |
| socket.io | قابلیت fallbacks، اتاقها (rooms)، سطح انتزاع بالا، مناسب برای توسعه سریع |
| uWebSockets.js | بسیار سریع و مقیاسپذیر، مناسب برای سیستمهای با نیاز به عملکرد بالا |
مثال پایه با کتابخانه ws
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws, req) {
console.log('New client connected:', req.socket.remoteAddress);
ws.on('message', function incoming(message) {
console.log('received: %s', message);
// پخش پیام به همهی کلاینتها
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
ws.send('Welcome to WebSocket server!');
});در این نمونه از کتابخانه ws استفاده شده و یک سرور WebSocket روی پورت 8080 ساختهایم. هنگام اتصال یک کلاینت، پیام خوشآمدگویی ارسال میشود. هر پیامی که از یک کلاینت دریافت شود به همهی کلاینتهای متصل Broadcast میشود. wss.clients مجموعهای از سشنهای فعال را نگهداری میکند.
مثال کلاینت (مرورگر)
// client.js (browser)
const socket = new WebSocket('ws://localhost:8080');
socket.addEventListener('open', function (event) {
console.log('Connected to server');
socket.send('Hello from client');
});
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});این کد در مرورگر اجرا میشود و به سرور WebSocket متصل میگردد، سپس پیام ارسال و پیامهای دریافتی را در کنسول لاگ میکند.
یکپارچهسازی با Express
اگر میخواهید WebSocket را کنار یک اپلیکیشن Express داشته باشید، میتوان سرور HTTP را مشترک کرد:
// express-ws.js
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
app.get('/', (req, res) => res.send('Hello HTTP'));
wss.on('connection', ws => {
ws.on('message', msg => ws.send(`Echo: ${msg}`));
});
server.listen(3000, () => console.log('Server listening on 3000'));در این الگو سرور HTTP و WebSocket از یک پورت مشترک استفاده میکنند که برای ترکیب APIهای REST و ارتباطات Real-time مناسب است.
نکات امنیتی و احراز هویت
- از wss (TLS) استفاده کنید: برای محیط تولید همیشه HTTPS/WSS فعال کنید.
- احراز هویت در handshake: توکن JWT یا کوکی را هنگام اتصال بررسی کنید.
- چک کردن Origin: خصوصاً برای کامپوننتهای عمومی، origin را اعتبارسنجی کنید.
نمونه بررسی توکن در handshake
// auth-ws.js (server-side)
const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws, req) {
const token = req.url.split('token=')[1];
try {
const payload = jwt.verify(token, 'your-secret');
ws.user = payload; // نگهداری اطلاعات کاربر
} catch (err) {
ws.close(4001, 'Authentication failed');
}
});در این مثال، توکن JWT از query string استخراج و در زمان اتصال بررسی میشود. در صورت نامعتبر بودن توکن، سشن بسته میشود. این یک روش ساده برای احراز هویت است؛ در پروژههای واقعی بهتر است توکن را از هدر یا کوکی امن خواند.
قابلیت مقیاسپذیری
WebSocket به خودی خود stateful است؛ برای مقیاسپذیری باید وضعیت ارتباطها بین چند سرور هماهنگ شود. راهکارهای معمول:
- استفاده از load balancer با sticky sessions (محدودیتها داشته باشد).
- استفاده از یک لایه pub/sub مانند Redis برای انتشار پیام بین نمونههای مختلف سرور.
- کتابخانههایی مثل socket.io-adapter-redis یا راهحلهای مبتنی بر Kafka برای بارهای بزرگ.
نمونه ساده با Redis Pub/Sub
// publisher.js
const Redis = require('ioredis');
const redis = new Redis();
redis.publish('chat', 'Hello from instance A');
// subscriber.js (another server instance)
const Redis = require('ioredis');
const redis = new Redis();
redis.subscribe('chat', (err, count) => {});
redis.on('message', (channel, message) => {
// پخش پیام به کلاینتهای متصل به این نمونه
});در این الگو هر نمونهی سرور که پیام جدیدی داشته باشد آن را در کانال Redis منتشر میکند و بقیه سرورها آن را دریافت و توزیع میکنند. این الگو برای اجرای Broadcast در فضاهای چند سروری ضروری است.
بهینهسازی و نکات عملی
- استفاده از ping/pong برای تشخیص اتصالهای مرده و جلوگیری از اتصالهای زامبی.
- مدیریت backpressure هنگام ارسال دادههای بزرگ یا زیاد.
- استفاده از فرمتهای باینری (مثل MessagePack یا protobuf) برای بهینهسازی حجم و سرعت.
- آزمایش تحت بار با ابزارهایی مانند Artillery یا wrk برای پیدا کردن گلوگاهها.
نمونه ساده heartbeat
// heartbeat.js
function noop() {}
function heartbeat() {
this.isAlive = true;
}
wss.on('connection', function connection(ws) {
ws.isAlive = true;
ws.on('pong', heartbeat);
});
const interval = setInterval(function ping() {
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping(noop);
});
}, 30000);این الگو با ارسال ping و منتظر ماندن برای pong، اتصالهای غیرفعال را تشخیص میدهد و آنها را پاک میکند تا منابع آزاد شوند.
نتیجهگیری و راهنمای عملی
WebSocket در Node.js ابزار قدرتمندی برای ساخت اپلیکیشنهای Real-time است. انتخاب کتابخانه مناسب، رعایت نکات امنیتی، برنامهریزی برای مقیاسپذیری و اجرای سیاستهای heartbeat و backpressure باعث میشود اپلیکیشن قابل اعتماد و سریع بماند. برای پروژههای ساده، ws یا socket.io کفایت میکند؛ برای نیازهای عملکردی بسیار بالا، uWebSockets.js یا معماری مبتنی بر pub/sub پیشنهاد میشود.
در توسعه، همیشه با تست بار، مانیتورینگ و بررسی لاگها پیش بروید تا رفتار در تولید عیان و قابل مدیریت باشد.
آیا این مطلب برای شما مفید بود ؟




