WebSocket على TypeScript: تطوير متقدم لتطبيقات الوقت الفعلي

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

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

ابدأ الدورة

WebSocket على TypeScript: بناء تطبيقات الوقت الفعلي المتقدمة

\n\n

أصبحت تطبيقات الوقت الفعلي جزءًا لا يتجزأ من الويب الحديث. من الدردشات والإشعارات إلى الألعاب عبر الإنترنت ومنصات التداول - توفر تقنية WebSocket اتصالاً ثنائي الاتجاه دائمًا بين العميل والخادم. على المستوى المتقدم، يتطلب تطوير هذه الأنظمة ليس فقط فهم البروتوكول، ولكن أيضًا القدرة على بناء بنى تحتية متسامحة مع الأخطاء وقابلة للتوسع. في هذه المقالة، سنشرح كيفية إنشاء تطبيقات WebSocket بجودة إنتاجية باستخدام TypeScript، مع أفضل الممارسات والأنماط.

\n\n

1. بنية الخادم وتجميع WebSocket

\n\n

خادم WebSocket واحد لن يتحمل حمولة مشروع حقيقي. المهمة الرئيسية هي تنظيم التجميع (Clustering) وتبادل الرسائل بين النسخ. سننظر في بنية باستخدام مكتبة ws وناقل أحداث Redis.

\n\n

سنقوم بتثبيت التبعيات:

\n
npm install ws redis ioredis @types/ws @types/ioredis
\n\n

سننشئ خادمًا مجمعًا أساسيًا:

\n\n
import { WebSocketServer, WebSocket } from 'ws';\nimport Redis from 'ioredis';\n\nconst redisPub = new Redis();\nconst redisSub = new Redis();\n\nconst wss = new WebSocketServer({ port: 8080 });\n\n// قناة للتواصل بين الخوادم\nconst CHANNEL = 'ws:messages';\n\nredisSub.subscribe(CHANNEL, (err, count) => {\n  if (err) console.error('خطأ في الاشتراك:', err);\n  console.log(`تم الاشتراك في ${count} قناة`);\n});\n\n// استقبال الرسائل من النسخ الأخرى\nredisSub.on('message', (channel, message) => {\n  const parsed = JSON.parse(message);\n  wss.clients.forEach((client) => {\n    if (client.readyState === WebSocket.OPEN) {\n      client.send(parsed.data);\n    }\n  });\n});\n\nwss.on('connection', (ws) => {\n  console.log('اتصال جديد');\n\n  ws.on('message', (data) => {\n    const message = data.toString();\n    // النشر في Redis لجميع النسخ\n    redisPub.publish(CHANNEL, JSON.stringify({ data: message }));\n  });\n});
\n\n

المزايا:

\n
    \n
  • التوسع الأفقي - أضف أي عدد تريده من الخوادم
  • \n
  • تحمل الأخطاء - عند تعطل نسخة واحدة، تستمر النسخ الأخرى في العمل
  • \n
  • ناقل رسائل موحد عبر Redis Pub/Sub
  • \n
\n\n

2. المصادقة وإدارة الجلسات

\n\n

في تطبيقات الإنتاج، لا يمكن الوثوق بالاتصالات غير الموثقة. سنقوم بتنفيذ التحقق من الرموز عبر JWT عند إنشاء الاتصال.

\n\n

سنقوم بتثبيت jsonwebtoken:

\n
npm install jsonwebtoken @types/jsonwebtoken
\n\n

سننشئ middleware لمصادقة WebSocket:

\n\n
import { IncomingMessage } from 'http';\nimport jwt from 'jsonwebtoken';\n\nconst JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';\n\ninterface AuthenticatedSocket extends WebSocket {\n  userId?: string;\n}\n\nfunction authenticate(ws: AuthenticatedSocket, req: IncomingMessage): boolean {\n  const url = new URL(req.url || '', 'http://localhost');\n  const token = url.searchParams.get('token');\n\n  if (!token) {\n    ws.close(4001, 'الرمز غير مقدم');\n    return false;\n  }\n\n  try {\n    const decoded = jwt.verify(token, JWT_SECRET) as { userId: string };\n    ws.userId = decoded.userId;\n    console.log(`المستخدم ${decoded.userId} تمت المصادقة عليه`);\n    return true;\n  } catch (err) {\n    ws.close(4002, 'رمز غير صالح');\n    return false;\n  }\n}\n\n// الاستخدام\nwss.on('connection', (ws: AuthenticatedSocket, req) => {\n  if (!authenticate(ws, req)) return;\n\n  ws.on('message', (data) => {\n    // الآن نعرف userId\n    console.log(`رسالة من ${ws.userId}: ${data}`);\n  });\n});
\n\n

التوصيات:

\n
    \n
  • قم بتمرير الرمز عبر معامل query أو الرؤوس عند إنشاء الاتصال
  • \n
  • استخدم رموز وصول قصيرة العمر ورموز تحديث
  • \n
  • قم بتخزين الجلسات في Redi

المدونات

توصيات الكتب