socket.emit لا يعمل بداخل socket.on

مرحبا , من جانب الخادم لدي هذه الأكواد

  socket.on("disconnect", function () {
    if (UserInfo[socket.id]) {
      socket.emit("SEND_EVENT_EMIT_SERVER", {
        cmd: "ev",
        data: 'window.onbeforeunload = null; location.href="/";'
      });
    }
  });

هذا الكود الذي في الأسفل يعمل بشكل ممتاز ومهمته هي تحديث الصفحة عند العميل

      socket.emit("SEND_EVENT_EMIT_SERVER", {
        cmd: "ev",
        data: 'window.onbeforeunload = null; location.href="/";'
      });

ولكن عندما أضعه في socket.on لا يعمل , ما هي المشكلة وهل هناك طريقة أخرى لتحديث الصفحة عند العميل ؟

يرجى الدخول لحسابك أو تسجيل حساب لتستطيع إضافة تعليق
حساب جديد دخول

التعليقات

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

اعتمد على حدث disconnecting بدلاً من disconnect، فهو يتم إطلاقه قبل أن يتم قطع الاتصال فعليًا، مما يمنحك الفرصة لإرسال أحداث إلى العميل.

مع استخدم طريقة broadcast لإرسال الحدث إلى جميع العملاء المتصلين، باستثناء العميل الذي سينقطع اتصاله.

للتوضيح:

socket.on("disconnecting", function () {
  if (UserInfo[socket.id]) {
    socket.broadcast.emit("SEND_EVENT_EMIT_SERVER", {
      cmd: "ev",
      data: 'window.onbeforeunload = null; location.href="/";'
    });
  }
});

لاحظ استخدام socket.broadcast.emit لإرسال الحدث إلى جميع العملاء المتصلين، باستثناء العميل الذي سيتم قطع اتصاله.

وفي حال تحتاج لإرسال الحدث إلى العميل نفسه قبل انقطاع الاتصال، حاول تجربة حدث disconnecting كالتالي:

socket.on("disconnecting", function () {
  if (UserInfo[socket.id]) {
    socket.emit("SEND_EVENT_EMIT_SERVER", {
      cmd: "ev",
      data: 'window.onbeforeunload = null; location.href="/";'
    });
  }
});

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

على جانب العميل، تقوم باستخدام مؤقت للتحقق بشكل دوري من الخادم لمعرفة ما إن كانت جلسته لا تزال نشطة، في حال استجاب الخادم بأن الجلسة لم تعد نشطة، يمكن للعميل إعادة توجيه نفسه إلى الـ URL المطلوب.

جانب الخادم:

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);


// تخزين جلسات المستخدم في مخزن مؤقت (مثل Redis)
const redis = require('redis');
const client = redis.createClient();


io.on('connection', (socket) => {
  const userId = socket.handshake.query.userId;
  client.set(`user:${userId}:connected`, true);


  socket.on('disconnect', () => {
    client.set(`user:${userId}:connected`, false);
  });
});

جانب العميل:

const socket = io();

setInterval(() => {
  fetch('/check-session')
    .then(response => response.json())
    .then((data) => {
      if (!data.connected) {
        window.location.href = '/';
      }
    });
}, 1000);

جانب الخادم - نقطة نهاية للتحقق من الجلسة

app.get('/check-session', (req, res) => {
  const userId = req.query.userId;
  client.get(`user:${userId}:connected`, (err, connected) => {
    res.json({ connected: connected === 'true' });
  });
});

للأسف قمت بتغييرها الى disconnecting ولم يعمل الكود أيضا ,

لا أعلم ما هو السبب كان يعمل سابقا وأما الآن فلم يعد يعمل

كما أنني قمت بتجربة تحويل socket.emit الى socket.broadcast.emit وتم تنفيد الكود ولكن لا يمكنني الاستمرار في استخدام socket.broadcast.emit لأنه يقوم بإعادة تحميل الصفحة عند جميع المستخدمين

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

بعد بحث ساعات متواصلة عثرت على المشكلة ووجدت الحل

كان علينا ان نضع كود إعادة تحميل الصفحة في مقبس مختلف من جهة الخادم على سبيل المثال

  socket.on('test', () => {
//my code
  });

وان نقوم بإستدعائها عند قطع الاتصال عبر disconnect من جهة العميل

 soket.on('disconnect', function () {
    x_x.emit('test');
  })

وسيتم تنفيذ الأكواد بنجاح

وشكرا لردك و لمساعدتك الدائمة

جيد أنك استطعت ايجاد المشكلة والحل بنفسك، وهذه هي أفضل طريقة للتعلم، توجد عدة طرق لحلّ المشكلة التي واجهتك فمثلا، يمكننا الاعتماد على مراقبة حالة الاتصال على جانب العميل واستخدام Socket.io Events بشكل فعال.

على جانب العميل نستخدم الكود التالي:

const socket = io();


// الاستماع لحدث قطع الاتصال
socket.on('disconnect', function (reason) {
  console.log('Disconnected:', reason);
  if (reason === 'io server disconnect') {
    // إذا تم فصل الاتصال من قبل الخادم، حاول إعادة الاتصال يدويًا
    socket.connect();
  }
});


// الاستماع لحدث إعادة المحاولة بعد الفشل في الاتصال
socket.on('reconnect_attempt', () => {
  console.log('Attempting to reconnect...');
});


// الاستماع لحدث الفشل في إعادة الاتصال
socket.on('reconnect_failed', () => {
  console.log('Failed to reconnect');
  window.onbeforeunload = null;
  location.href = "/";
});


// الاستماع لحدث انقطاع الاتصال بسبب فشل المحاولات
socket.on('reconnect_error', (error) => {
  console.log('Reconnect error:', error);
  window.onbeforeunload = null;
  location.href = "/";
});


// الاستماع لحدث نجاح إعادة الاتصال
socket.on('reconnect', (attemptNumber) => {
  console.log('Reconnected on attempt:', attemptNumber);
});


ومن جانب الخادم:

io.on('connection', function (socket) {
  socket.on('disconnect', function (reason) {
    if (UserInfo[socket.id]) {
      console.log(`User ${socket.id} disconnected due to ${reason}`);    }
  });
});

فمن جانب العميل، يتم الاستماع لحدث disconnect لمراقبة سبب قطع الاتصال فإذا كان سبب انقطاع الاتصال هو io server disconnect، يحاول العميل إعادة الاتصال يدويا.

ثم يتم الاستماع لكل من حدث reconnect_attempt للإبلاغ عن محاولة إعادة الاتصال، وحدث reconnect_failed للإبلاغ عن فشل جميع محاولات إعادة الاتصال، وفي هذه الحالة يتم إعادة تحميل الصفحة.

كذلك بالنسبة لحدث reconnect_error للإبلاغ عن حدوث خطأ في إعادة الاتصال، وفي هذه الحالة يتم إعادة تحميل الصفحة أيضا.

يتم الاستماع لحدث reconnect للإبلاغ عن نجاح إعادة الاتصال.

أما على جانب الخادم يتم الاستماع لحدث disconnect للإبلاغ عن سبب قطع الاتصال.

نعم هذا صحيح وفكرة جيدة يمكننا الاعتماد عليها

شكرا لك