WebSockets والتطبيقات الفورية
إعداد خادم Socket.io
إعداد خادم Socket.io
Socket.io هي المكتبة الأكثر شيوعًا لتنفيذ اتصالات WebSocket في تطبيقات Node.js. توفر طبقة اتصال ثنائية الاتجاه قوية وموثوقة في الوقت الفعلي تعمل حتى في ظروف الشبكة الصعبة.
تثبيت Socket.io
أولاً، قم بإنشاء مشروع Node.js جديد وتثبيت التبعيات المطلوبة:
# إنشاء مجلد المشروع
mkdir socketio-server
cd socketio-server
# تهيئة مشروع npm
npm init -y
# تثبيت التبعيات
npm install express socket.io
ملاحظة: يتطلب Socket.io خادم HTTP من Node.js للعمل. سنستخدم Express لأنه يوفر طريقة نظيفة لإنشاء الخادم ومعالجة مسارات HTTP العادية جنبًا إلى جنب مع اتصالات WebSocket.
إنشاء خادم Socket.io أساسي
لنقم بإنشاء خادم بسيط يستمع لاتصالات WebSocket:
// server.js
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
// إنشاء تطبيق Express
const app = express();
// إنشاء خادم HTTP
const httpServer = createServer(app);
// إنشاء خادم Socket.io
const io = new Server(httpServer);
// تقديم الملفات الثابتة (اختياري)
app.use(express.static('public'));
// معالجة مسارات HTTP
app.get('/', (req, res) => {
res.send('<h1>خادم Socket.io يعمل</h1>');
});
// الاستماع لاتصالات WebSocket
io.on('connection', (socket) => {
console.log('مستخدم متصل:', socket.id);
// معالجة قطع الاتصال
socket.on('disconnect', () => {
console.log('مستخدم قطع الاتصال:', socket.id);
});
});
// بدء الخادم
const PORT = 3000;
httpServer.listen(PORT, () => {
console.log(`الخادم يعمل على http://localhost:${PORT}`);
});
فهم حدث الاتصال
يتم تشغيل حدث connection كلما نجح عميل في الاتصال بالخادم. كل اتصال يتلقى كائن socket فريد يمثل هذا العميل المحدد:
io.on('connection', (socket) => {
// socket.id - معرف فريد لهذا الاتصال
console.log('اتصال جديد:', socket.id);
// socket.handshake - تفاصيل الاتصال
console.log('المصافحة:', socket.handshake.address);
console.log('الرؤوس:', socket.handshake.headers);
// socket.connected - حالة الاتصال المنطقية
console.log('متصل:', socket.connected);
});
إرسال الأحداث من الخادم
يمكن للخادم إرسال أحداث للعملاء باستخدام دالة emit():
io.on('connection', (socket) => {
// الإرسال إلى هذا العميل المحدد
socket.emit('welcome', 'مرحبًا بك في الخادم!');
// الإرسال إلى جميع العملاء
io.emit('userCount', io.engine.clientsCount);
// الإرسال إلى جميع العملاء عدا المرسل
socket.broadcast.emit('newUser', socket.id);
});
نصيحة: استخدم
socket.emit() للإرسال لعميل واحد، io.emit() للإرسال لجميع العملاء، و socket.broadcast.emit() للإرسال لجميع العملاء عدا المرسل.
الاستماع للأحداث من العملاء
استخدم socket.on() للاستماع للأحداث المخصصة من العملاء:
io.on('connection', (socket) => {
// الاستماع لرسالة الدردشة
socket.on('chatMessage', (data) => {
console.log('رسالة مستلمة:', data);
// البث لجميع العملاء
io.emit('chatMessage', {
id: socket.id,
message: data.message,
timestamp: new Date()
});
});
// الاستماع لمؤشر الكتابة
socket.on('typing', (isTyping) => {
socket.broadcast.emit('userTyping', {
userId: socket.id,
isTyping: isTyping
});
});
// الاستماع للأحداث المخصصة
socket.on('customEvent', (payload) => {
console.log('حدث مخصص:', payload);
});
});
خيارات تكوين الخادم
يوفر Socket.io العديد من خيارات التكوين لتخصيص السلوك:
const io = new Server(httpServer, {
// تكوين CORS
cors: {
origin: 'http://localhost:5173',
methods: ['GET', 'POST'],
credentials: true
},
// مهلة الاتصال
connectTimeout: 45000,
// مهلة الـ ping
pingTimeout: 5000,
pingInterval: 10000,
// الحد الأقصى لحجم مخزن HTTP المؤقت
maxHttpBufferSize: 1e6, // 1 ميجابايت
// السماح بالترقيات
allowUpgrades: true,
// وسائل النقل
transports: ['websocket', 'polling'],
// خيارات الـ cookie
cookie: {
name: 'io',
httpOnly: true,
path: '/'
}
});
التعامل مع CORS في Socket.io
عندما يكون عميلك وخادمك على أصول مختلفة، تحتاج إلى تكوين CORS:
// السماح بأصل محدد
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:5173',
methods: ['GET', 'POST']
}
});
// السماح بأصول متعددة
const io = new Server(httpServer, {
cors: {
origin: [
'http://localhost:5173',
'http://localhost:3001',
'https://myapp.com'
],
credentials: true
}
});
// السماح بجميع الأصول (للتطوير فقط!)
const io = new Server(httpServer, {
cors: {
origin: '*'
}
});
تحذير: لا تستخدم أبدًا
origin: '*' في بيئة الإنتاج! حدد دائمًا الأصول المسموح بها صراحة لمنع الثغرات الأمنية.
معالجة الأخطاء
تعامل مع الأخطاء بشكل صحيح لمنع تعطل الخادم:
io.on('connection', (socket) => {
// معالجة أخطاء الاتصال
socket.on('error', (error) => {
console.error('خطأ في السوكيت:', error);
});
// معالجة قطع الاتصال مع السبب
socket.on('disconnect', (reason) => {
console.log('قطع الاتصال:', reason);
if (reason === 'transport close') {
// فقدان الاتصال
} else if (reason === 'ping timeout') {
// لم يستجب العميل لـ ping
}
});
});
// معالجة الأخطاء على مستوى الخادم
io.engine.on('connection_error', (err) => {
console.log('خطأ في الاتصال:', err.code);
console.log('الرسالة:', err.message);
console.log('السياق:', err.context);
});
مثال كامل للخادم
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:5173',
methods: ['GET', 'POST']
}
});
// تتبع المستخدمين المتصلين
const connectedUsers = new Map();
io.on('connection', (socket) => {
console.log(`مستخدم متصل: ${socket.id}`);
// إضافة المستخدم للتتبع
connectedUsers.set(socket.id, {
connectedAt: new Date(),
lastActivity: new Date()
});
// إرسال عدد المستخدمين الحالي
io.emit('userCount', connectedUsers.size);
// معالجة رسائل الدردشة
socket.on('message', (data) => {
connectedUsers.get(socket.id).lastActivity = new Date();
io.emit('message', {
id: socket.id,
message: data,
timestamp: new Date()
});
});
// معالجة قطع الاتصال
socket.on('disconnect', () => {
console.log(`مستخدم قطع الاتصال: ${socket.id}`);
connectedUsers.delete(socket.id);
io.emit('userCount', connectedUsers.size);
});
});
httpServer.listen(3000, () => {
console.log('الخادم يعمل على المنفذ 3000');
});
تمرين: أنشئ خادم Socket.io يقوم بـ:
- تتبع عدد العملاء المتصلين
- إرسال رسالة ترحيب للاتصالات الجديدة مع معرف السوكيت الخاص بهم
- بث إشعار عندما ينضم مستخدم أو يغادر
- تسجيل جميع أحداث الاتصال وقطع الاتصال مع الطوابع الزمنية
- تنفيذ تكوين CORS مناسب لـ localhost:5173