क्या सॉकेट्स पर n क्लाइंट्स के साथ संचार करने वाला टर्न-आधारित सर्वर लिखने का कोई पैटर्न है?


14

मैं एक सामान्य गेम सर्वर पर काम कर रहा हूं जो एक गेम खेलने वाले टीसीपी सॉकेट-नेटवर्क क्लाइंट की एक मनमानी संख्या के लिए गेम का प्रबंधन करता है। मेरे पास डक्ट-टेप के साथ मिलकर एक 'डिज़ाइन' हैक किया गया है जो काम कर रहा है, लेकिन यह नाजुक और अनम्य है। क्या क्लाइंट / सर्वर संचार लिखने के लिए एक अच्छी तरह से स्थापित पैटर्न है जो मजबूत और लचीला है? (यदि नहीं, तो आपके पास मेरे नीचे क्या सुधार होगा?)

मोटे तौर पर मेरे पास यह है:

  • गेम सेट करते समय सर्वर में क्लाइंट से सिंक्रोनस रिक्वेस्ट को हैंडल करने वाले प्रत्येक प्लेयर सॉकेट के लिए एक थ्रेड होता है और सर्वर से रिस्पॉन्स होता है।
  • एक बार खेल हो रहा है, हालांकि, एक नींद को छोड़कर सभी धागे, और उस धागे को एक बार में सभी खिलाड़ियों के माध्यम से अपनी चाल (उलट अनुरोध-प्रतिक्रिया में) के बारे में बताते हुए।

यहाँ वर्तमान में मेरे पास जो कुछ है उसका एक चित्र है; बड़े / पठनीय संस्करण, या 66kB पीडीएफ के लिए क्लिक करें ।

प्रवाह का अनुक्रम आरेख

समस्या:

  • इसके लिए खिलाड़ियों को सही संदेश के साथ वास्तव में प्रतिक्रिया देने की आवश्यकता होती है। (मुझे लगता है कि मैं प्रत्येक खिलाड़ी को यादृच्छिक बकवास के साथ जवाब दे सकता हूं और केवल एक बार वे मुझे एक वैध कदम देते हैं।)
  • यह खिलाड़ियों को सर्वर से बात करने की अनुमति नहीं देता है जब तक कि उनकी बारी नहीं है। (मैं सर्वर को उन्हें अन्य खिलाड़ियों के बारे में अपडेट भेज सकता था, लेकिन एक एसिंक्रोनस अनुरोध को संसाधित नहीं कर सकता था।)

अंतिम आवश्यकताएं:

  • प्रदर्शन सर्वोपरि नहीं है। यह ज्यादातर गैर-रियलटाइम गेम के लिए उपयोग किया जाएगा, और ज्यादातर एक दूसरे के खिलाफ एआई को थोपने के लिए, न कि चिकोटी मनुष्यों के लिए।

  • गेम प्ले हमेशा टर्न-बेस्ड होगा (भले ही बहुत उच्च रिज़ॉल्यूशन पर)। प्रत्येक खिलाड़ी को हमेशा एक कदम आगे बढ़ने से पहले सभी खिलाड़ियों को एक बारी मिलती है।

सर्वर का कार्यान्वयन रूबी में होता है, अगर इससे कोई फर्क पड़ता है।


यदि आप प्रति ग्राहक एक टीसीपी सॉकेट का उपयोग करते हैं, तो यह टोपी (65535-1024) ग्राहकों के लिए नहीं होगी?
ओ ० '।

1
क्यों यह एक समस्या है, लोहोरियों? अधिकांश लोग 6000 समवर्ती उपयोगकर्ताओं को प्राप्त करने के लिए संघर्ष करेंगे, कभी भी 60000 का मन नहीं करेंगे।
काइलोटन

@ कियलोतन वास्तव में। अगर मेरे पास 10 से अधिक समवर्ती AI हैं, तो मुझे आश्चर्य होगा। :)
फ्रॉग्ज़

जिज्ञासा से बाहर, आपने उस आरेख को बनाने के लिए किस उपकरण का उपयोग किया?
मथियास

@matthias दुख की बात है, कि Adobe Illustrator में बहुत अधिक कस्टम काम था।
मेंढक

जवाबों:


10

मुझे यकीन नहीं है कि यह वही है जो आप हासिल करना चाहते हैं। लेकिन, एक ऐसा पैटर्न है जो गेम सर्वर में लगातार उपयोग किया जाता है, और आपकी मदद कर सकता है। संदेश कतारों का उपयोग करें।

अधिक विशिष्ट होने के लिए: जब ग्राहक सर्वर पर संदेश भेजते हैं, तो उन्हें तुरंत संसाधित न करें। बल्कि, उन्हें पार्स करें और इस विशिष्ट ग्राहक के लिए एक कतार में डालें। फिर, कुछ मुख्य लूप में (शायद एक और धागे पर भी) बारी-बारी से सभी ग्राहकों के ऊपर जाते हैं, कतार से संदेश प्राप्त करते हैं और उन्हें संसाधित करते हैं। जब प्रसंस्करण इंगित करता है कि इस ग्राहक की बारी समाप्त हो गई है, तो अगले पर जाएं।

इस तरह, ग्राहकों को सख्ती से बारी-बारी से काम करने की आवश्यकता नहीं है; केवल इतनी तेजी से कि आप कतार में कुछ है जब तक ग्राहक संसाधित हो जाता है (आप, निश्चित रूप से, या तो ग्राहक के लिए इंतजार कर सकते हैं या अपनी बारी छोड़ सकते हैं अगर यह पिछड़ जाता है)। और आप "async" कतार जोड़कर अतुल्यकालिक अनुरोधों के लिए समर्थन जोड़ सकते हैं: जब कोई ग्राहक एक विशेष अनुरोध भेजता है, तो उसे इस विशेष कतार में जोड़ा जाता है; इस कतार की जाँच की जाती है और अधिक बार ग्राहकों को संसाधित किया जाता है।


1

हार्डवेयर थ्रेड्स पर्याप्त रूप से "प्रति खिलाड़ी" बनाने के लिए पर्याप्त नहीं हैं, जो 3-अंकों की संख्या वाले खिलाड़ियों के लिए एक उचित विचार है, और यह जानने का तर्क है कि उन्हें कब जगाया जाए, यह एक जटिलता है जो बढ़ेगा। एक बेहतर विचार रूबी के लिए एक अतुल्यकालिक I / O पैकेज खोजना है जो आपको पूरे प्रोग्राम थ्रेड को रोकने के बिना डेटा भेजने और प्राप्त करने की अनुमति देगा, जबकि पढ़ने या लिखने का ऑपरेशन होता है। इससे खिलाड़ियों को जवाब देने के लिए इंतजार करने की समस्या का भी हल हो जाता है, क्योंकि आपके पास पढ़ने के लिए कोई धागा नहीं होगा। इसके बजाय आपका सर्वर यह देख सकता है कि क्या समय सीमा समाप्त हो गई है और फिर उसके अनुसार अन्य खिलाड़ी को सूचित करें।

मूल रूप से 'अतुल्यकालिक I / O' वह 'पैटर्न' है जिसकी आप तलाश कर रहे हैं, हालांकि यह वास्तव में एक पैटर्न नहीं है, एक दृष्टिकोण है। सॉकेट पर स्पष्ट रूप से 'रीड' कॉल करने और डेटा आने तक प्रोग्राम को पॉज़ करने के बजाय, जब भी आपका डेटा तैयार होता है, तो आप अपने 'ऑनरेड' हैंडलर को कॉल करने के लिए सिस्टम सेट करते हैं, और आप उस समय तक प्रोसेसिंग करते रहते हैं।

प्रत्येक सॉकेट की बारी है

प्रत्येक खिलाड़ी की बारी है, और प्रत्येक खिलाड़ी के पास एक सॉकेट है जो डेटा भेजता है, जो थोड़ा अलग है। एक दिन आपको प्रति खिलाड़ी एक सॉकेट नहीं चाहिए। आप सॉकेट्स का उपयोग बिल्कुल नहीं कर सकते हैं। जिम्मेदारी के क्षेत्रों को अलग रखें। क्षमा करें यदि यह एक महत्वहीन विवरण की तरह लगता है, लेकिन जब आप अपने डिजाइन में अवधारणाओं को भ्रमित करते हैं जो अलग होना चाहिए तो बेहतर दृष्टिकोणों को खोजना और चर्चा करना कठिन हो जाता है।


1

निश्चित रूप से इसे करने का एक से अधिक तरीका है, लेकिन व्यक्तिगत रूप से, मैं अलग-अलग थ्रेड्स को पूरी तरह से छोड़ दूंगा, और बस एक इवेंट लूप का उपयोग करूंगा। आपके ऐसा करने का तरीका आपके द्वारा उपयोग किए जा रहे I / O लाइब्रेरी पर कुछ हद तक निर्भर करेगा, लेकिन मूल रूप से, आपका मुख्य सर्वर लूप इस तरह दिखाई देगा:

  1. नए कनेक्शन के लिए एक कनेक्शन पूल और एक सुनने वाला सॉकेट स्थापित करें।
  2. कुछ होने की प्रतीक्षा करें।
  3. यदि कुछ नया कनेक्शन है, तो इसे पूल में जोड़ें।
  4. यदि किसी ग्राहक से कुछ अनुरोध है, तो जांचें कि क्या यह ऐसा कुछ है जिसे आप तुरंत संभाल सकते हैं। यदि हाँ, तो ऐसा करें; यदि नहीं, तो इसे एक कतार में रखें और (वैकल्पिक रूप से) ग्राहक को एक पावती भेजें।
  5. यह भी जांचें कि क्या कतार में कुछ ऐसा है जिसे आप अभी संभाल सकते हैं; यदि ऐसा है, तो करें।
  6. चरण 2 पर लौटें।

उदाहरण के लिए, मान लें कि आपके पास एक गेम में शामिल एन क्लाइंट हैं। जब उनमें से पहले n of 1 अपनी चाल में भेजते हैं, तो आप बस यह देखते हैं कि यह कदम वैध लग रहा है और यह कहते हुए संदेश वापस भेज देता है कि यह कदम प्राप्त हो गया है लेकिन आप अभी भी अन्य खिलाड़ियों के स्थानांतरित होने की प्रतीक्षा कर रहे हैं। सभी n खिलाड़ियों के चले जाने के बाद , आप उन सभी चालों को संसाधित करते हैं, जिन्हें आपने सहेजा है और सभी खिलाड़ियों को परिणाम भेजते हैं।

आप टाइमआउट को शामिल करने के लिए भी इस योजना को परिष्कृत कर सकते हैं - अधिकांश I / O पुस्तकालयों में प्रतीक्षा के लिए कुछ तंत्र होना चाहिए जब तक कि नया डेटा नहीं आ जाता है या एक निश्चित समय बीत चुका है।

बेशक, आप प्रत्येक कनेक्शन के लिए अलग-अलग थ्रेड्स के साथ कुछ इस तरह से लागू कर सकते हैं, उन थ्रेड्स को किसी भी अनुरोध पर पास करने से वे सीधे केंद्रीय थ्रेड (या तो प्रति गेम या एक प्रति सर्वर) पर हैंडल नहीं कर सकते हैं जो लूप चलाता है ऊपर दिखाया गया है, सिवाय इसके कि यह ग्राहकों के साथ सीधे कनेक्शन के थ्रेडर्स के साथ बात करता है। चाहे आप इस सरल या एकल-थ्रेड दृष्टिकोण की तुलना में अधिक जटिल हैं, वास्तव में आप पर निर्भर है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.