كيفية القيام بهذا الخاص بين التطبيقات الخاصة عبر الشبكة؟

StackOverflow https://stackoverflow.com/questions/390944

سؤال

إنه تطبيق واحد أساسا مثبتا على جهاز كمبيوتر متعدد الاستخدامات، كل تثبيت يحافظ على قاعدة البيانات الخاصة به التي تتم مزامنة مع الآخر كما لو كانت متابعة (متصلة بنفس الشبكة) في نفس الوقت.

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

ما هي الطريقة العادية / القياسية للقيام بهذا التطبيق To-App، أين أجد المزيد؟

أيضا، ما هي التقنيات / التي يمكن استخدامها للإعلان والعثور على التطبيقات الأخرى على الشبكة؟


تحرير: (تكرير مشكلتي)

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

يبدو أيضا أنني بحاجة إلى إنشاء اتصال P2P بمجرد العثور على تطبيقين أو أكثر بعضا البعض - كيف أفعل ذلك؟

إذا كانت هناك أمثلة / برامج تعليمية متاحة، فيرجى توجيهها. مشاريع / وحدات صغيرة مفتوحة المصدر التي تنفذ شيئا مثل ما أحتاج إليه ستخدم أيضا.

منصة اختياري هي Linux، لكن الأمثلة القائمة على Windows ستكون صالحة للاستخدام للغاية.


تحرير [09-01-06]:

أنا أتطلع حاليا إلى الخيارات التالية:

  1. المتعدد المتعدد (TLDP-HOWTO) - هذا يبدو عملا، لكنني بحاجة إلى دراسة ذلك أكثر من ذلك.
  2. باستخدام خوادم DNS الديناميكية الحرة، على الرغم من أن هذا يبدو ديسي قليلا ...
  3. باستخدام بعض مرفق البريد الإلكتروني المجاني، على سبيل المثال Gmail / Yahoo / ...، وإرسال / قراءة البريد من هناك للعثور على IP التطبيقات الأخرى (يمكن أن تعمل، ولكن يشعر القذرة)
  4. تم اقتراح مواقع الويب، لكنني لا أعرف كيف يعملون وسيتعين عليهم دراسة الأمر

وأود أن أقدر رأيك في هذه الخيارات وإذا كان هناك أي أمثلة هناك. لسوء الحظ، ليس لديك خيار استخدام خادم أو موقع مركزي (ما لم يكن مضمونا أن تكون حرة ودائمة).

تحرير 2009-02-19

(أتمنى أن أقبل إجابات اثنين / ثلاث إجابات! واحد قبلته لأنه يوفر خطوط التفكير والإمكانيات، في حين جاء آخرون مع حلول ثابتة، ولكنها قابلة للتطبيق، شكرا لجميع الذين أجابوا، كل ذلك يساعد.)

كما وعندما أجد / تنفذ حلاي، سأقوم بتحديث هذا السؤال، وينبغي أن يكون الحل كافيا، سأقوم بإنشاء مشروع SourceForge لذلك. (في أي حال مشكلة صغيرة في مشروع أكبر بكثير.)

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

المحلول

همم،

هذا يشبه مشكلة الرياضيات قليلا. سؤال كيف اثنين من أجهزة الكمبيوتر إنشاء اتصال بمجرد أن يجدوا بعضهم البعض واضحا إلى حد ما. يمكنك استخدام أي عدد من بروتوكولات P2P أو خادم العميل. SSL. هو متاح عالميا تقريبا ولكن يمكنك أيضا أن تخدم SSH., ، يركض فرينيت أو أيا كان. بمجرد إنشاء اتصال من خلال أحد هذه البروتوكولات، نموذج نشر / اشترك ل تبادل البيانات يمكن أن تعمل بشكل جيد. ولكن هناك

مسألة كيفية العثور على أجهزة الكمبيوتر بعضها البعض هي حيث تصبح الأمور صعبة. هناك أساسا ثلاث حالات:

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

  2. أجهزة الكمبيوتر الخاصة بك في العقد التعسفية على الإنترنت ** و ** سيكون هناك دائما واحدة على الأقل من أجهزة الكمبيوتر الخاصة بك المتصلة بالويب ** و ** جميع الآلات تذهب عبر الإنترنت بشكل منتظم.في هذه الحالة، يمكن لكل جهاز الحفاظ على قائمة تشغيل عناوين IP. عندما تعود جهازا غير متصل للإنترنت لفترة من الوقت، فهو يتحقق من العناوين المعروفة، والاتصال بعنوان صحيح الأول - هكذا يكتشف بروتوكول Emule الخوادم.

  3. أجهزة الكمبيوتر الخاصة بك في العقد التعسفية على الإنترنت ** و ** جميع الآلات غير متصل معا ** أو ** عدد قليل جدا من الذهاب دون اتصال لفترات طويلة أثناء التبديل Outeers عناوين IP. هنا، لا أعتقد أنه يمكنك إظهار أنه لا توجد وسيلة للآلات الجديدة التي تأتي في العثور على بعضها البعض بدون بعض الخادم المركزي يرتبط مع IPS المشترك. لا توجد طريقة لبث رسالة عبر الإنترنت لأنها كبيرة. في هذه الظروف، ليس لدى أجهزة الكمبيوتر معلومات تحديد الأجهزة الأخرى التي تأتي عبر الإنترنت حتى تحتاج إلى استخدام مورد مركزي مشترك: عنوان البريد الإلكتروني الذي ذكرته، موقع ويب، خادم FTP، قناة IRC. DNS الديناميكي هو مجرد مثيل واحد آخر من متجر معلومات مركزية. إذا كان يجب عليك استخدام مثل هذا المتجر، فعمنا بطبيعة الحال تقييم جميع المتاجر المتاحة لموثوقيتها والسرعة والدوام. ما لم تقدم المزيد من المعلومات حول ما يحتاجه طلبك في هذا الصدد، لا أعتقد أن أي شخص آخر يمكن أن يقرر أي متجر دائم تحتاج إليه.

أعتقد أن هذا يغطي كل الاحتمالات. ما عليك القيام به هو تحديد أي من هذه الحالات العامة يسقط طلبك في ظل ثم اتخاذ قرار بشأن أحد البروتوكولات التي تناسب هذه الحالة.

يحرر:

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

  • الموثوقية: كم مرة هو الشيء في يوم معين
  • السرعة: كيف بسرعة يستجيب الشيء
  • الدوام: كم من الوقت تتوقع أن تستمر شيء؟ هل تفقد الوصول إليها كما يتطور الإنترنت؟

كما ذكر، هناك العديد من الموارد المتاحة بسهولة أكثر أو أقل تناسب هذا القانون. خوادم البريد الإلكتروني (كما كنت تستخدم حاليا)، خوادم الويب، خوادم FTP، خوادم DNS، قنوات IRC، حسابات Twitter، منتديات الويب ....

مشكلة التطبيقات التي تأتي إلى الحياة بعد فترة من الوقت وتتطلب التحديث دون خادم مركزي هي مشكلة شائعة ولكنها شائعة في الغالب بين كاتب الفيروسات - فقط حول أي مؤسسة لها الموارد اللازمة لإنشاء تطبيق موزز لديها أيضا موارد للحفاظ على مركزي الخادم. ومع ذلك، فإن الحلول القياسية على مر السنين تضمن البريد الإلكتروني خوادم HTTP وخوادم FTP وقنوات IRC وخوادم DNS الديناميكية. تختلف خوادم مختلفة في كل فئة من هذه الفئات في سرعتها والموثوقية والديمتان لذا فإن مهمة اختيار المرء يعود إلى حكمك. تستحق قنوات IRC ذكر لأنها سريعة وسهلة الإعداد ولكن هذه قد تتلاشى في الواقع مع تطور الإنترنت.

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

فقط لتكرار. أتصور أن هناك ثلاثة أجزاء لمشكلتك:

  1. الآلات التي تجد بعضها البعض (انظر أعلاه)
  2. الآلات التي تنشئ اتصال (مرة أخرى، SSL، SSH وآخرون متاحون بسهولة)
  3. آلات تبادل البيانات. يمكنك استخدام نموذج "نشر / اشتراك" أو لفة فقط بسيطة بروتوكول. وبعد عملت للشركة التي لديها عميل التحديث التلقائي وهذا ما فعلناه. أسباب إنشاء بروتوكولك الخاص هي 1) بحتى أبسط المواقف، ستغير المتطلبات الخاصة بالسرعة والموثوقية كما ستختلف البيانات التي تبادلها، 2. يتطلب أبسط تبادل البيانات فقط بضعة أسطر من التعليمات البرمجية ولذا لا يزعج أحد البروتوكولات لتبادل البيانات البسيط حقا، 3. نظرا لأن التطبيقات المختلفة تستخدم طرق مختلفة ولغات مختلفة، لا يوجد بروتوكول لتبادل البيانات البسيط هو المهيمنة. للحصول على مواقف أكثر تعقيدا، هناك غابة كاملة من البروتوكولات، لكن تعقيدها المختلف ستجعلها محرجة لاستخدامها مقابل تبادل البيانات البسيط. الطريقة التي جيت SCM. يرسل البيانات هو مثال واحد على بروتوكول التحديث. إذا حدث ذلك، فهذا يحدث لك يشبه قاعدة البيانات شفرة المصدر التي يرسلها git، فيمكنك استخدام git للحفاظ على قاعدة البيانات الخاصة بك. لكن الفرص هي أن نهج التحديث الخاص بك لن يشبه ما يفعله الجيت عن كثب. بروتوكول مثال آخر هو إصدار واحد أو آخر من خدمات الويب مثل الصابون. هذه البروتوكولات فقط لف عملية استدعاء وظيفة على جهاز واحد باستخدام XML و HTTP. إذا كنت تستطيع بالفعل إنشاء اتصال مأخذ التوصيل بين تطبيقاتك، فلا يوجد سبب للقيام بذلك. تذكر، لتنفيذ خدمات الويب التي تحتاج إلى تشغيل خادم HTTP وتحليل XML لعملاء HTTP في بيانات RAW. بالنظر إلى أنه يمكنك إرسال بياناتك مباشرة من خلال مأخذ توصيل، لا يوجد سبب للقيام بذلك. لذلك أنت تعود إلى المتداول بنفسك.

على أي حال، قد يكون مثال على بروتوكول بسيط:

  • يرسل تطبيق واحد أولا فهرس البيانات له كصفيف من الفهارس.
  • يقوم التطبيق الآخر بإرسال قائمة العناصر التي هي جديدة
  • ثم يقوم التطبيق الأول بإرسال هذه العناصر الفعلية.

ثم تغيير التطبيقات أدوار وتبادل البيانات في الاتجاه الآخر. ستبدو "مصافحة" معينة في بروتوكولك مثل هذا في رمز الزائفة:

void update_database(in_stream, out_stream) {
  Get_Index_Of_Other_Machines_Items(in_stream);
  Send_Index_Of_Items_You_Need(out_stream);
  Get_Items_You_Need(in_stream);
}

مع وظيفة أخرى لتنفيذ الجانب الآخر من هذه التبادلات البيانات.

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

لن تضطر بلا شك إلى القرص وتطويرها بشكل أكبر إذا كنت ستستخدمها.

ضع في اعتبارك ذلك إذا كانت التطبيقات الخاصة بك كلها تحديث فقط في بعض الأحيان, لن تكون هناك طريقة لكونها متأكدة من أن أي حالات معينة سيكون لها أي عنصر بيانات. على سبيل المثال، لنفترض، في يوم واحد، نصف الآلات فقط تذهب عبر الإنترنت بعد 5:00 مساء والنصف الآخر فقط تذهب عبر الإنترنت قبل 5:00 مساء. في هذه الحالة، ستقوم مجموعتي الآلات بمشاركة أي بيانات على الإطلاق إذا قاموا بتحديث بعضهم البعض مباشرة. من ناحية أخرى، من ناحية أخرى، تقوم أجهزتك حقا في الأوقات الموزعة (بدلا من النموذج كما هو موضح)، فمن المحتمل بشكل معقول أن تحصل كل آلة في نهاية المطاف على جميع التحديثات (على الأقل جميع التحديثات القديمة) وبعد

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

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

نصائح أخرى

يرى نشر الاشتراك نموذج المراسلة غير المتزامن.

تطبيق مثال هو أباتشي ActiveMQ.:

Apache ActiveMQ سريع، يدعم العديد من عملاء اللغة والبروتوكولات عبر الإنترنت، ويأتي مع سهولة استخدام أنماط تكامل المؤسسات والعديد من الميزات المتقدمة أثناء الدعم الكامل JMS 1.1 و J2EE 1.4.

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

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

ثم، أقوم بإعداد وظائف مختلفة "BroadcastMessage ()" التي ستبث أحداث معينة. لقد ذهبت حتى بالنسبة للمطورين المسموح بهم باستخدام API، فإن القدرة على إنشاء أحداث مخصصة، مع بيانات الحمولة المخصصة، ثم قم بتسجيل البرنامج مستمعا، ولهذا الحدث، مما أدى إلى إبلاغ المسجل عندما جاء هذا الحدث، وتمريه البيانات التي جاءت معها.

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

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

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

من السهل حقا إعداد مستمع على مؤشر ترابط يستمع فقط لهذه الأنواع من الرسائل ... إذا كنت بحاجة إلى مثال رمز، يمكنني تقديم ذلك أيضا، لكنه في C ++، ومصمم لنظام Windows، ولكنه يستخدم WSOCK32 الخام .LIB، لذلك يجب أن ينتقل إلى أي منصة UNIX سهلة للغاية. (فقط تحتاج إلى typedef dword، كما اعتدت أن الكثير ..).

لقد حل هذه المشكلة عدة مرات في إدارة الشبكة. يبدو أن قلقك الأساسي هو "اكتشاف"، كيف تكتشف تطبيقاتك بعضها البعض.

بصراحة أسهل طريقة هي معرفة عنوان IP الخاص بك وقناع (معظم الفئة C)، ومحاولة الاتصال بكل جهاز في هذا الفئة C.

إذا قمت بالتراضي للفئة C، فهذا يعني أنه سيكون دائما يعمل دائما لمعظم الشبكات. يمكنك بعد ذلك السماح بالتجاوز حيث تضيف إما عناوين IP محددة للاتصال أو الشبكات الفرعية الإضافية.

لاكتشاف الفئة C، يمكنك فقط معرفة عنوان IP الخاص بك (دعنا نقول 192.168.2.77)، ثم تكرر كل شيء في 192.168.2 (1-254)، في محاولة لفتح اتصال لكل منهما.

لقد فعلت ذلك مع مؤشرات الترابط متعددة (يمكنك الحصول على كل الأجهزة في وقت واحد بهذه الطريقة ولها نتائج جيدة في غضون 3 ثوان. اكتشفت شبكة فئة B في مثل 5 دقائق مع بضع مئات من المواضيع!)، أو يمكنك الذهاب للتو من واحد إلى آخر في مؤشر ترابط واحد - ولكن إذا قمت بذلك تأكد من أن مهلةك منخفضة حقا (1/2 ثانية أو نحو ذلك) وإلا فسوف يستغرق الأمر إلى الأبد - حتى في 1/2 ثانية سوف يستغرق دقيقة واحدة لجعل الجولات.

ربما تريد أيضا أن تغادر مؤشر ترابط الاكتشاف في الخلفية بسرعة أقل، والبحث دائما عن زملاء جديدة.

وذاكرة التخزين المؤقت الخاص بك عناوين IP "المعروفة" الخاصة بك لبدء التشغيل السريع.

إنها ليست مشكلة صعبة، لكنها ليست تافهة أيضا. فقط نتوقع أن تفعل القليل من العقار.

أيضا، ربما ترغب في أن تكون قادرا على إضافة IP / قناع جديد لمسح الشبكة الفرعية الخارجية. لا توجد وسيلة فقط حول هذا إذا كنت ترغب في الاتصال بالأجهزة على الإنترنت (على الرغم من أنه بمجرد اكتشاف جهاز كمبيوتر واحد شبكة، فيمكنه إرسال العنوان إلى جميع الآخرين إذا كنت تريد، ويمكن أن ينمو بالفعل بسرعة كبيرة حقا!)

هل تريد أن يكون هذا p2p تماما أو هل تخطط لخادم مركزي لفعل أي شيء أكثر من ذلك، كونه دليلا؟

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

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

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

// Some defines that you may see in the code, all of which are user defined...
#define ADVERTISE_SERVER           0x12345678 // Some unique ID for your advertisement server
#define ACTIVITY_NONE              0x00000000
#define ACTIVITY_LOGON             0x00000001
#define ACTIVITY_LOGOFF            0x00000002
#define ACTIVITY_RUNNING           0x00000004
#define ACTIVITY_IDLE              0x00000005
#define ACTIVITY_SPECIFIC          0x00000006


enum Advertisements {
   ADVERTISE_SHUTDOWN,
   ADVERTISE_MESSAGE,
   ADVERTISE_DEBUG,
   ADVERTISE_OVERLAPPED,
   ADVERTISE_BROADCAST_IDENTITY,
   ADVERTISE_IDENTITY,
   ADVERTISE_PARAMETER_CHANGE
};

struct TAdvertiseServerPacket {
   UINT     uiAdvertisePacketType;
   DWORD    dwPacketLength;
   bool     bRequestReply;
   UINT     uiReplyType;
   bool     bOverlappedResult;
   int      iPacketId;
   bool     bBroadcast;
   char     GuidHash[35];
   BYTE     PacketData[1024];
};

struct TAdvertiseIdentity {
   TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
   char  szUserName[LEN_APPL_USERNAME + 1];
   char  szDatabase[MAX_PATH];
   char  szConfiguration[MAX_PATH];
   char  szVersion[16];
   long  nUserId;
   char  szApplication[MAX_PATH];
   char  szActivity[33];
   UINT  uiStartupIndc;
};

struct TAdvertiseMessage {
   char              MessageFrom[LEN_APPL_USERNAME + 1];
   char              MessageText[512];
};

struct TAdvertiseItemUpdate {
   NMHDR             pNMHDR;
   long              nItemId;
   long              nItemTypeId;
   char              szItemName[LEN_ITEM_NAME + 1];
   bool              bState;
};

struct TAdvertiseItemUpdateEx {
   NMHDR             pNMHDR;
   long              nItemId;
   bool              bState;
   bool              bBroadcast;
   DWORD             dwDataSize;
   void              *lpBuffer;
};

struct TOverlappedAdvertisement {
   int               iPacketId;
   BYTE              Data[1020];
};

DWORD WINAPI CAdvertiseServer::Go(void* tptr)
{
   CAdvertiseServer *pThis = (CAdvertiseServer*)tptr;

   /* Used and reused for Overlapped results, */
   DWORD BufferSize       = 0;
   BYTE *OverlappedBuffer = NULL;
   bool bOverlapped       = false;
   int  iOverlappedId     = 0;
   DWORD BufferPosition   = 0;
   DWORD BytesRecieved    = 0;
   TAdvertiseItemUpdateEx *itemex = NULL;
   UINT uiPacketNumber    = 0;

   bool Debug = false;
#ifdef _DEBUG
   Debug = true;
#endif
   {
      DWORD dwDebug = 0;
      dwDebug = GetParameter(ADVERTISE_SERVER_DEBUG); // GetParameter is part of the main program used to store running config values.
      if(dwDebug > 0)
      {
         Debug = true;
      }
   }
   WSAData wsaData;
   WSAStartup(MAKEWORD(1,1), &wsaData);
   ServerSocket = socket(PF_INET, SOCK_DGRAM, 0);
   if(ServerSocket == INVALID_SOCKET)
   {
      CLogging Log("Client.log");
      ServerSocket = NULL;
      Log.Log("Could not create server advertisement socket: %d", GetLastError());
      return -1;
   }
   sockaddr_in sin;
   ZeroMemory(&sin, sizeof(sin));
   sin.sin_family = AF_INET;
   sin.sin_port = htons(Port);
   sin.sin_addr.s_addr = INADDR_ANY;
   if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
   {
      CLogging Log("Client.log");
      Log.Log("Could not bind server advertisement socket on port: %d Error: %d", Port, GetLastError());
      DWORD dwPort = 0;
      dwPort = GetParameter(ADVERTISE_SERVER_PORT); // Again, used to set the port number, if one could not be figured out.
      if(dwPort > 0)
      {
         return -1;
      }
      Port = 36221;
      sin.sin_port = htons(Port);
      if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
      {
         CLogging Log("Client.log");
         Log.Log("Could not bind server advertisement socket on port: %d Error: %d Could not start AdvertiseServer after two attempts.  Server failed.", Port, GetLastError());
         return -1;
      }
   }

   SECURITY_ATTRIBUTES sa;
   sa.bInheritHandle = TRUE;
   sa.lpSecurityDescriptor = NULL;
   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
   HANDLE mutex = CreateMutex(NULL, FALSE, "Client.Mutex"); // Used to keep and eye on the main program, if it shuts down, or dies, we need to die.
   while (1)
   {
      TAdvertiseServerPacket ap;
      sockaddr_in sin;
      int fromlen = sizeof(sin);
      fd_set fds;
      FD_ZERO(&fds);
      FD_SET(ServerSocket, &fds);
      timeval tv;
      tv.tv_sec = 15;
      tv.tv_usec = 0;
      int err = select(0, &fds, NULL, NULL, &tv);
      if(err == SOCKET_ERROR)
      {
         CLogging Log("Client.log");
         Log.Log("Advertise: Winsock error: %d", WSAGetLastError());
         Beep(800, 100);
         break;
      }
      if(err == 0)
      {
         if(WaitForSingleObject(mutex, 0) != WAIT_OBJECT_0)
         {
            continue; // Main app is still running
         }
         else
         {
            Beep(800, 100); // Main app has died, so exit our listen thread.
            break;
         }
      }

      int r = recvfrom(ServerSocket, (char *)&ap, sizeof(ap), 0, (sockaddr *)&sin, &fromlen);

      if(r != sizeof(TAdvertiseServerPacket))
      {
         continue;
      }
      switch(ap.uiAdvertisePacketType)
      {
         // This is where you respond to all your various broadcasts, etc.
         case ADVERTISE_BROADCAST_IDENTITY:
         {
            // None of this code is important, however you do it, is up to you.
            CDataAccess db(CDataAccess::DA_NONE);
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            if(pThis->szActivity) {
               CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_SPECIFIC, pThis->szActivity, false);
            } else {
               CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_RUNNING, NULL, false);
            }
         }
         case ADVERTISE_IDENTITY:
         {
            TAdvertiseIdentity ident;
            memcpy((void*)&ident, (void*)ap.PacketData, ap.dwPacketLength);
            Listener::iterator theIterator;
            theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
            if(theIterator == pThis->m_Listeners.end())
            {

               //We got an Identity Broadcast, but we're not listening for them.
               continue;
            }
            {
               itemex = new TAdvertiseItemUpdateEx;
               ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
               memcpy((void*)&ident, ap.PacketData, ap.dwPacketLength);
               itemex->pNMHDR.code     = (*theIterator).first;
               itemex->pNMHDR.hwndFrom = (*theIterator).second;
               itemex->pNMHDR.idFrom   = ADVERTISE_SERVER;
               itemex->dwDataSize      = sizeof(TAdvertiseIdentity);
               itemex->lpBuffer        = (void*)&ident;
               SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
               delete itemex;
            }
         }
         case ADVERTISE_SHUTDOWN:
         {
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            CString guid;
            guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
            if(stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0)
            {
               return 1;
            }
         }
         case ADVERTISE_MESSAGE:
         {
            TAdvertiseMessage msg;
            memcpy((void*)&msg, (void*)ap.PacketData, ap.dwPacketLength);
            CString msgtext;
            msgtext.Format("Message from: %s\r\n\r\n%s", msg.MessageFrom, msg.MessageText);
            ::MessageBox(NULL, msgtext, "Broadcast Message", MB_ICONINFORMATION | MB_SYSTEMMODAL);
            break;
         }
         case ADVERTISE_OVERLAPPED:
         {
            // I left this code in here, as it's a good example of how you can send large amounts of data over a UDP socket, should you need to do it.
            BufferPosition = (1020 * ((ap.uiReplyType - 1) - 1));
            if(BufferPosition > BufferSize) {
               BufferPosition -= 1020;
            }
            TOverlappedAdvertisement item;
            ZeroMemory(&item, sizeof(TOverlappedAdvertisement));
            memcpy((void*)&item, (void*)ap.PacketData, ap.dwPacketLength);
            if(item.iPacketId == iOverlappedId)
            {
               DWORD ToCopy = (sizeof(item.Data) > (BufferSize - BytesRecieved) ? BufferSize - BytesRecieved : sizeof(item.Data));
               memcpy((void*)&OverlappedBuffer[BufferPosition], (void*)item.Data, ToCopy);
               BytesRecieved += ToCopy;
               if(BytesRecieved < BufferSize)
               {
                  continue;
               }
            }
         }
         default:
         {
            // What do we do if we get an advertisement we don't know about?
            Listener::iterator theIterator;
            if(bOverlapped == false)
            {
               theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
               if(theIterator == pThis->m_Listeners.end())
               {
                  continue;
               }
            }

            // Or it could be a data packet
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            CString guid;
            guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
            bool FromUs = stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0;
            if(((FromUs && Debug) || !FromUs) || ap.bBroadcast)
            {
               if(ap.bOverlappedResult)
               {
                  if(ap.uiReplyType == 1)
                  {
                     itemex = new TAdvertiseItemUpdateEx;
                     ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
                     memcpy(itemex, ap.PacketData, ap.dwPacketLength);
                     OverlappedBuffer = (BYTE*)malloc(itemex->dwDataSize);
                     BufferSize = itemex->dwDataSize;
                     ZeroMemory(OverlappedBuffer, itemex->dwDataSize);
                     bOverlapped = true;
                     iOverlappedId = ap.iPacketId;
                     uiPacketNumber = ap.uiReplyType;
                  }
                  continue;
               }
               if(bOverlapped)
               {
                  itemex->pNMHDR.code     = (*theIterator).first;
                  itemex->pNMHDR.hwndFrom = (*theIterator).second;
                  itemex->pNMHDR.idFrom   = ADVERTISE_SERVER;
                  itemex->dwDataSize      = BufferSize;
                  itemex->lpBuffer        = (void*)OverlappedBuffer;
                  SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
                  delete itemex;
                  free(OverlappedBuffer);
                  BufferSize       = 0;
                  OverlappedBuffer = NULL;
                  bOverlapped      = false;
                  iOverlappedId    = 0;
                  BufferPosition   = 0;
                  BytesRecieved    = 0;
                  itemex           = NULL;
                  uiPacketNumber   = 0;
                  break;
               }
               TAdvertiseItemUpdate *item = new TAdvertiseItemUpdate;
               ZeroMemory(item, sizeof(TAdvertiseItemUpdate));
               memcpy(item, ap.PacketData, ap.dwPacketLength);

               item->pNMHDR.code     = (*theIterator).first;
               item->pNMHDR.hwndFrom = (*theIterator).second;
               item->pNMHDR.idFrom   = ADVERTISE_SERVER;
               SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)item);
               delete item;
            }
            break;
         }
      }
   }
   try {
      ResetEvent(ServerMutex);
      CloseHandle(pThis->ServerMutex);
      closesocket(ServerSocket);
      return 0;
   }
   catch(...) {
      closesocket(ServerSocket);
      return -2;
   }
}

// Here's a couple of the helper functions that do the sending...
bool CAdvertiseServer::SendAdvertisement(TAdvertiseServerPacket packet)
{
   TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
   ZeroMemory(ComputerName, sizeof(ComputerName));
   DWORD len = MAX_COMPUTERNAME_LENGTH;
   GetComputerName(ComputerName, &len);
   CString guid;
   guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);

   strcpy(packet.GuidHash, CDataAccess::HashPassword(guid));

   bool bRetval = false;
   SOCKET s = socket(PF_INET, SOCK_DGRAM, 0);
   if(s != INVALID_SOCKET)
   {
      BOOL tru = TRUE;
      setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&tru, sizeof(tru));
      sockaddr_in sin;
      ZeroMemory(&sin, sizeof(sin));
      sin.sin_family = PF_INET;
      sin.sin_port = htons(Port);
      sin.sin_addr.s_addr = INADDR_BROADCAST;
      if(sendto(s, (char *)&packet, sizeof(packet), 0, (sockaddr *)&sin, sizeof(sin)) > 0)
      {
         bRetval = true;
         if(packet.bRequestReply)
         {
           // Here is where your work comes in, in setting up a reply, or making a TCP connection back to the other client.
         }
      }
      closesocket(s);
   }
   return bRetval;
}

bool CAdvertiseServer::Advertise(UINT uiAdvertisement, long nItemId, bool bState, void *lpBuffer, DWORD dwDataSize, bool bBroadcast)
{
   TAdvertiseServerPacket packet;
   ZeroMemory(&packet, sizeof(packet));
   TAdvertiseItemUpdateEx   item;
   ZeroMemory(&item, sizeof(item));

   UINT packetnum = 1;
   packet.bOverlappedResult = true;
   packet.bRequestReply = false;
   packet.uiAdvertisePacketType = uiAdvertisement;
   packet.dwPacketLength = sizeof(item);
   packet.uiReplyType = packetnum;
   packet.bBroadcast = bBroadcast;
   item.nItemId = nItemId;
   item.bState  = bState;
   item.dwDataSize = dwDataSize;
   memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
   packet.iPacketId = GetTickCount();
   if(SendAdvertisement(packet))
   {
      BYTE *TempBuf = new BYTE[dwDataSize];
      memcpy(TempBuf, lpBuffer, dwDataSize);

      DWORD pos = 0;
      DWORD BytesLeft = dwDataSize;
      while(BytesLeft)
      {
         TOverlappedAdvertisement item;
         packet.uiAdvertisePacketType = ADVERTISE_OVERLAPPED;
         packet.bOverlappedResult = BytesLeft > 1020;
         item.iPacketId = packet.iPacketId;
         memcpy((void*)item.Data, (void*)&TempBuf[pos], (BytesLeft >= 1020 ? 1020 : BytesLeft));
         memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
         packet.dwPacketLength = sizeof(item);
         packet.uiReplyType++;
         if(SendAdvertisement(packet))
         {
            if(BytesLeft >= 1020)
            {
               BytesLeft -= 1020;
               pos += 1020;
            }
            else
            {
               BytesLeft = 0;
            }
         }
      }
      delete TempBuf;
   }
   return true;
}

void CAdvertiseServer::Shutdown()
{
   TAdvertiseServerPacket packet;
   packet.uiAdvertisePacketType = ADVERTISE_SHUTDOWN;
   SendAdvertisement(packet);
}

حسنا - MQ وهذا النوع من الأشياء يبدو وكأنه أكثر من قتل.

فهمي لتطبيقك:

تطبيق سطح المكتب يعمل على أجهزة متعددة على نفس الشبكة - لديك قواعد بيانات خاصة بها، تحتاج إلى اكتشاف بعضها البعض.

لما لا:

1) البث / استمع UDP بشكل منتظم إلى "العثور على أجهزة أخرى على نفس الشبكة" - مثال في Java: http://java.sun.com/docs/Books/tutorial/networking/datagrams/index.html.

2) استخدام مآخذ SSL للاتصال الفعلي بعد اكتشاف:
http://stilius.net/java/java_ssl.php.....
http://www.exampledepot.com/egs/javax.net.ssl/client.html.

هل فكرت في استخدام برنامج إعداد مكتوب Bittorrent؟

يجب أن يمنحك مديرو الاتصالات المستخدمة قاعدة صلبة إلى حد ما لبناء طلبك. كل ما تحتاجه هو لعقدتين لمعرفة بعضهما البعض ثم يبني من هناك. أنا أستعمل مونورنت لتشغيل شبكة بيانات خاصة (100 عقدة)، فإن تغذية RSS للإعلان عن الملفات التي تحتاج إلى أن تكون حيث (نسخة معدلة من WordPress) وتفعل كل شيء في أنفاق SSH. لدي خادم مركزي يدير الشبكة، ولكن هذا يمكن أن يعيش بسهولة على أي عقد مني 100. باستخدام خدمة DNS الديناميكية، تقوم العقدة الأولى بحيا في إعداد تعقبها الخاص كنسخة احتياطية إذا انخفض الخادم الخاص بي.

يمكنك استخدام ملفات XML كخطط المراسلة الخاصة بك، أو تعديل ناقل حركة شبكة Bittorrent لإرسال حزم البيانات مباشرة إلى تطبيقك. أعتقد أن مفهوم ما تبحث عنه هو في تورنت. ستعيد العقدة الأولى لإطلاق إدخال DNS الديناميكي (Dyndns. لديه سهلة إلى حد ما للاستخدام api.) إذا لم يكن هناك مضيف نشط على الشبكة. (هناك جانب سلبي ... لقد واجهت مشكلات مزامنة عندما أطلق اثنان من المتتبعين داخل نافذة TTL)

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

أعرف أن الأفكار هي نوع من مختلط، آمل أن يعطيك القليل من المساعدة في توجيه نفسك في الاتجاه الصحيح. ملاحظة ... للحصول على حل محمول بالكامل، يمكنك تشغيل هذا في Java أو .NET (يعمل تحت أحادي .. لدي AppleTVS تشغيل أحادي حتى). ثم يمكن لنظام التشغيل حتى جزء مرن من العملية الخاصة بك.

يبدو أنك بحاجة إلى ذاكرة التخزين المؤقت الموزعة أو وظيفة DB غير المتصلة - اعتمادا على لغتك (Java / C # / ...) هناك خيارات مختلفة مفتوحة لك ...

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

ربما فاتني شيئا هنا، لكنني فشلت في رؤية اختيارك لغرفة برمجة. في بيئة قائمة على Windows، باستخدام .NET Framework، سيكون أفضل خيار يستخدم WCF، والذي يسمح لك بإضافة الأمان / المتانة بتكوين بسيط. إذا كنت تريد حلا مع أجهزة الكمبيوتر المستندة إلى Windows والتي ليست موجهة نحو المنحى، فسأطلع إلى استخدام MSMQ، وهو إطار اتصال بنيت لهذه المعايير.

هناك مقال جيد حول P2P مع WCF هناhttp://msdn.microsoft.com/en-us/magazine/cc188685.aspx.وبعد يوفر رمز ولكن يفترض .NET3، WCF، ويندوز فيستا وما فوق

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