WebSocket في JavaScript: تطبيقات متقدمة في الوقت الفعلي

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: ما وراء أساسيات WebSocket

الفهم الأساسي لـ WebSocket يتيح لك إنشاء دردشة بسيطة أو إشعارات. ومع ذلك، لبناء تطبيقات real-time بمستوى إنتاجي - ألعاب متعددة اللاعبين، محطات مالية، أنظمة تحرير مشترك - تحتاج إلى إتقان تقنيات متقدمة. في هذه المقالة، سنتجاوز حدود "Hello, World" ونتناول كيفية بناء تطبيقات WebSocket موثوقة وقابلة للتوسع وآمنة باستخدام Node.js.

سنغطي أربعة مجالات رئيسية: التوسع باستخدام Redis، إدارة الحالات والغرف، الأمان والترخيص، والتجميع للعمل على معالجات متعددة. جميع أمثلة الكود مكتوبة بلغة JavaScript باستخدام مكتبة ws و Node.js.

1. التوسع باستخدام Pub/Sub (Redis)

أحد القيود الرئيسية لخادم WebSocket هو أنه لا يمكنه "التواصل" مع مثيلات أخرى. إذا كان لديك خادمان يعملان خلف موازن تحميل، فإن العميل المتصل بالخادم A لن يتلقى رسالة مرسلة عبر الخادم B. الحل هو استخدام نمط Pub/Sub (الناشر-المشترك) القائم على Redis.

يعمل Redis كوسيط مركزي: كل خادم يشترك في قناة وينشر الرسائل فيها. بهذه الطريقة، تنتشر الرسالة المرسلة من أي خادم فورًا إلى جميع الخوادم الأخرى.

const WebSocket = require('ws');
const redis = require('redis');

const wss = new WebSocket.Server({ port: 8080 });
const publisher = redis.createClient();
const subscriber = redis.createClient();

// الاشتراك في قناة 'broadcast'
subscriber.subscribe('broadcast');
subscriber.on('message', (channel, message) => {
  // إرسال الرسالة إلى جميع العملاء المتصلين
  wss.clients.forEach(client => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(message);
    }
  });
});

wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    // نشر الرسالة في Redis
    publisher.publish('broadcast', data.toString());
  });
});

هذا النهج يسمح لك بتوسيع تطبيقك أفقيًا بشكل غير محدود تقريبًا. عند إضافة خادم جديد، يقوم ببساطة بإنشاء اشتراكه الخاص في Redis.

2. إدارة الغرف والحالات

في معظم تطبيقات real-time، تحتاج إلى تجميع العملاء في "غرف" (غرف دردشة، صالات ألعاب) وإدارة حالتهم. الخاصية الأساسية wss.clients لا توفر هذه الوظيفة. سنقوم بتنفيذ نظام غرف بسيط لكنه فعال.

سننشئ كائن rooms حيث المفتاح هو اسم الغرفة والقيمة هي Set من عملاء WebSocket. سنضيف طرقًا للدخول والخروج والإرسال داخل الغرفة.

const rooms = new Map();

function joinRoom(ws, roomName) {
  if (!rooms.has(roomName)) {
    rooms.set(roomName, new Set());
  }
  rooms.get(roomName).add(ws);
  // حفظ الغرفة في كائن ws للوصول السريع
  ws.room = roomName;
}

function leaveRoom(ws) {
  if (ws.room && rooms.has(ws.room)) {
    rooms.get(ws.room).delete(ws);
    if (rooms.get(ws.room).size === 0) {
      rooms.delete(ws.room);
    }
    delete ws.room;
  }
}

function broadcastToRoom(roomName, message, exclude = null) {
  if (!rooms.has(roomName)) return;
  rooms.get(roomName).forEach(client => {
    if (client !== exclude && client.readyState === WebSocket.OPEN) {
      client.send(message);
    }
  });
}

const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    const msg = JSON.parse(data);
    switch (msg.type) {
      case 'join':
        joinRoom(ws, msg.room);
        break;
      case 'message':
        broadcastToRoom(ws.room, msg.text, ws);
        break;
    }
  });
  ws.on('close', () => leaveRoom(ws));
});

لإدارة الحالة (مثل موقع اللاعب، المؤشر)، يمكنك تخزين البيانات في Map

المدونات

توصيات الكتب