سؤال

أنا تصميم خادم اللعبة مع إمكانيات النصية. التصميم العام يذهب مثل هذا:

Client connects to Server,
Server initializes Client,
Server sends Client to EventManager (separate thread, uses libevent),
EventManager receives receive Event from Client socket,
Client manages what it received via callbacks.

الآن الجزء الأخير هو ما هو الأكثر صعوبة بالنسبة لي الآن.

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

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

ما يحدث هو أنه عندما يقول شيء ما في أي مكان في رد الاتصال هذا، افصل هذا ()، وأريد قطع الاتصال على الفور Client, ، إزالته من EventManager, وأخيرا إزالته من Server, ، حيث يجب أن تحصل أيضا على الذاكرة المدمر والخالية أخيرا. ومع ذلك، لا يزال لدي بعض الرمز قيد التشغيل بعد انتهاء الاتصال في العميل، وبالتالي لا يمكنني تحرير الذاكرة.

ماذا يجب أن أتغير في التصميم؟ يجب أن يكون لدي حدث توقيت في Server التي الشيكات التي ClientS مجانية لتدمير؟ هل أن تخلق النفقات العامة الإضافية التي لا أحتاج إليها؟ هل لا تزال بخير بعد انتهاء معاودة الاتصال لتشغيل الحد الأدنى من التعليمات البرمجية على المكدس (return -1;) أم لا؟

ليس لدي أي فكرة عما يجب القيام به، لكنني منفتح على تجديد التصميم الكامل.

شكرا مقدما.

هل كانت مفيدة؟

المحلول

يمكنك استخدام مرجع محاصر مؤشر مثل boost::shared_ptr<> لتبسيط إدارة الذاكرة. إذا كانت قائمة عميل المدير تستخدم shared_ptrS والرمز الذي يستدعي الاحتياطات يخلق نسخة محلية من shared_ptr يتم استدعاء الاتصال، وسيبقى الكائن حيا حتى تتم إزالةه من المدير و وظيفة رد الاتصال كاملة:

class EventManager {
  std::vector< boost::shared_ptr<Client> > clients;

  void handle_event(Event &event) {
    // local |handler| pointer keeps object alive until end of function, even
    // if it removes itselfe from |clients|
    boost::shared_ptr<Client> handler = ...;
    handler->process(event);
  }
};

class Client {
  void process(Event &event) {
    manager->disconnect(this);
    // the caller still holds a reference, so the object lives on
  }
}

ال Client سيتم حذف الكائن تلقائيا مرة واحدة shared_ptr لذلك يخرج من النطاق، ولكن ليس من قبل. لذلك إنشاء نسخة محلية من shared_ptr قبل استدعاء الوظيفة، يتأكد من عدم حذف الكائن بشكل غير متوقع.

نصائح أخرى

يجب أن تفكر في وجود كائن مثل "جلسة" مما سيتتبع تدفق رسالة معينة من البداية إلى النهاية (من عميل واحد). يجب أن يهتم هذا الكائن أيضا بالدولة الحالية: في المقام الأول المخازن المؤقتة والمعالجة. كل حدث يؤدي إلى تحديث رد الاتصال يجب تحديث حالة الجلسة المقابلة. Libvent قادر على تزويدك بأي نتيجة للحدث المجدول: النجاح، الفشل، مهلة. يجب أن تنعكس كل من هذه الأنواع مع منطقك. بشكل عام، عند العمل مع الأحداث، فكر في منطق المعالجة لتكون Automaton مع دولة.

http://en.wikipedia.org/wiki/reactor_pattern. قد يكون موردا جيدا لمهمتك.

دع عميل :: Disconnect () دالة إرسال حدث إلى فئة EventManager (أو الخادم). هذا يعني أنك بحاجة إلى نوع من التعامل مع الأحداث في EventManager (أو Server)، وحلقة حدث على سبيل المثال.

فكرتي العامة هي أن العميل :: Disconnect () لا يفصل العميل على الفور، ولكن فقط بعد الانتهاء من الاتصال بالتنفيذ. بدلا من ذلك، فإنه فقط ينشر حدثا إلى فئة EventManager (أو الخادم).

يمكن للمرء أن يجادل طريقة تشغيل العميل :: Disconnect () في الفصل الخطأ. ربما يجب أن يكون الخادم :: قطع الاتصال (العميل * ج). سيكون ذلك أكثر تركيزا مع فكرة أن الخادم "يملك" العميل وهو الخادم الذي يقوم بفصل العملاء (ثم يقوم بتحديث بعض مسك الدفاتر الداخلية).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top