Soketler üzerinden n istemciyle iletişim kuran sıra tabanlı bir sunucu yazmak için bir kalıp var mı?


14

Oyun oynayan isteğe bağlı TCP soket ağa bağlı istemciler için oyunları yöneten genel bir oyun sunucusu üzerinde çalışıyorum. Çalışan bir koli bandı ile birlikte kesmek bir 'tasarım' var, ama hem kırılgan hem de esnek görünmüyor. Sağlam ve esnek istemci / sunucu iletişiminin nasıl yazılacağı konusunda köklü bir kalıp var mı? (Değilse, aşağıda sahip olduğum şeyi nasıl geliştirirdiniz?)

Kabaca ben var:

  • Bir oyun kurarken, sunucu bir istemciden senkron istekleri ve sunucudan gelen yanıtları işleyen her oyuncu soketi için bir iş parçacığına sahiptir.
  • Bununla birlikte, oyun devam ettikten sonra, bir uyku haricindeki tüm evreler ve bu evre hareketleri hakkında iletişim kurarak tüm oyuncular arasında birer birer döngü yapar (tersine çevrilmiş istek-yanıtında).

İşte şu anda sahip olduğum şema; daha büyük / okunabilir versiyon için tıklayınız veya 66kB PDF .

Akış sırası diyagramı

sorunlar:

  • Oyuncuların tam olarak doğru mesajla cevap vermelerini gerektirir. (Her oyuncunun rastgele saçmalıklarla yanıt vermesine izin verebileceğimi ve sadece geçerli bir hamle yaptıktan sonra devam edebileceğini düşünüyorum.)
  • Oyuncuların sırası gelmedikçe sunucuyla konuşmasına izin vermez. (Sunucunun diğer oyuncular hakkında güncelleme göndermesini sağlayabilirim, ancak eşzamansız bir isteği işleme koyamıyordum.)

Son gereksinimler:

  • Performans çok önemli değil. Bu çoğunlukla gerçek zamanlı olmayan oyunlar için ve çoğunlukla yapay zekaları seğirme insanlarına değil, birbirine çarpmak için kullanılır.

  • Oyun her zaman sıra tabanlı olacaktır (çok yüksek çözünürlükte bile olsa). Diğer tüm oyuncular bir tur atmadan önce her oyuncu bir hamle alır.

Bir fark yaratırsa, sunucunun uygulanması Ruby'de olur.


İstemci başına bir TCP soketi kullanırsanız, bu kapak (65535-1024) istemciler için geçerli olmaz mı?
o0 '.

1
Bu neden bir sorun, Lohoris? Çoğu insan 60000 eşzamanlı kullanıcı almak için mücadele edecek,
60000'ü

@Kylotan Gerçekten. 10'dan fazla eşzamanlı AI'm varsa şaşırırım. :)
Phrogz

Meraktan, bu diyagramı oluşturmak için hangi aracı kullandınız?
matthias

@matthias Ne yazık ki, bu Adobe Illustrator'da çok fazla özel bir işti.
Mart'ta Phrogz

Yanıtlar:


10

Tam olarak ne elde etmek istediğinizden emin değilim. Ancak, oyun sunucularında sürekli olarak kullanılan ve size yardımcı olabilecek bir model vardır. Mesaj kuyruklarını kullanın.

Daha açık olmak gerekirse: istemciler sunucuya ileti gönderdiğinde, bunları hemen işlemeyin. Aksine, bunları ayrıştırın ve bu belirli istemci için bir sıraya koyun. Sonra, bazı ana döngüde (belki başka bir iş parçacığında bile) sırayla tüm istemcilerin üzerinden geçin, kuyruktan iletileri alın ve bunları işleyin. İşlem, bu istemcinin sırasının bittiğini belirtirse, bir sonrakine geçin.

Bu şekilde, müşterilerin kesinlikle adım adım çalışması gerekmez; istemcinin işlenmesi sırasında kuyrukta bir şeye sahip olmanız için yeterince hızlı (tabii ki, istemciyi bekleyebilir veya gecikirse sırasını atlayabilirsiniz). Ayrıca, "async" kuyruğu ekleyerek eşzamansız istekler için destek ekleyebilirsiniz: istemci özel bir istek gönderdiğinde, bu özel kuyruğa eklenir; bu kuyruk kontrol edilir ve müşterilerinkinden daha sık işlenir.


1

Donanım iş parçacıkları, "oyuncu başına bir tane" yi 3 basamaklı oyuncular için makul bir fikir haline getirecek kadar iyi ölçeklenmez ve bunları ne zaman uyandıracağını bilmenin mantığı büyüyecek bir karmaşıklıktır. Daha iyi bir fikir, okuma veya yazma işlemi gerçekleşirken tüm program iş parçasını duraklatmak zorunda kalmadan veri göndermenize ve almanıza olanak tanıyan Ruby için eşzamansız bir G / Ç paketi bulmaktır. Bu aynı zamanda oyuncuların cevap vermesini beklemek sorununu da çözer, çünkü bir okuma işleminde asılı hiçbir parçacığa sahip olmazsınız. Bunun yerine sunucunuz bir süre sınırının dolup dolmadığını kontrol edebilir ve ardından diğer oynatıcıyı buna göre bilgilendirebilir.

Temelde 'asenkron I / O', aradığınız 'kalıp', aslında bir kalıp olmasa da, daha fazla bir yaklaşım. Bir sokette açıkça 'read' çağrısı yapmak ve veriler gelene kadar programı duraklatmak yerine, sistemi veri hazır olduğunda 'onRead' işleyicinizi arayacak şekilde ayarladınız ve o zamana kadar işleme devam ediyorsunuz.

her soket bir dönüşe sahiptir

Her oyuncunun bir dönüşü vardır ve her oyuncunun veri gönderen bir soketi vardır, bu da biraz farklıdır. Bir gün oyuncu başına bir yuva istemeyebilirsiniz. Hiç soket kullanamayabilirsiniz. Sorumluluk alanlarını ayrı tutun. Bu, önemsiz bir ayrıntı gibi görünüyorsa, ancak tasarımınızdaki farklı olması gereken kavramları birleştirdiğinizde, daha iyi yaklaşımları bulmayı ve tartışmayı zorlaştırır.


1

Bunu yapmak için kesinlikle birden fazla yol var, ama kişisel olarak, ayrı konuları tamamen atlarım ve sadece bir olay döngüsü kullanırım. Bunu yapma şekliniz, kullandığınız G / Ç kütüphanesine bağlı olacaktır, ancak temel olarak ana sunucu döngünüz aşağıdaki gibi görünecektir:

  1. Yeni bağlantılar için bir bağlantı havuzu ve bir dinleme soketi kurun.
  2. Bir şey olmasını bekleyin.
  3. Bir şey yeni bir bağlantıysa, havuza ekleyin.
  4. Bir şey bir istemciden gelen bir istekse, hemen ele alabileceğiniz bir şey olup olmadığını kontrol edin. Cevabınız evet ise; değilse, bir kuyruğa koyun ve (isteğe bağlı olarak) istemciye bir bildirim gönderin.
  5. Ayrıca kuyrukta şimdi halledebileceğiniz bir şey olup olmadığını kontrol edin; eğer öyleyse yap.
  6. 2. adıma dönün.

Örneğin, bir oyunda yer alan n müşteriniz olduğunu varsayalım. O zaman ilk n − 1 tanesi hamlelerinde gönderdiğinde, hamlenin geçerli göründüğünü kontrol edersiniz ve hamlenin alındığını ancak yine de diğer oyuncuların hareket etmesini beklediğinizi belirten bir mesaj gönderirsiniz. Sonuçta n oyuncu taşındıktan sonra, kaydettiğiniz tüm hareketleri işler ve sonuçları tüm oyunculara gönderirsiniz.

Bu şemayı zaman aşımlarını içerecek şekilde daraltabilirsiniz - çoğu G / Ç kütüphanesinde yeni veriler gelene kadar beklemek için bazı mekanizmalar olmalıdır veya belirli bir süre .

Elbette, her bir bağlantı için ayrı ayrı iş parçacıkları ile böyle bir şey uygulayabilirsiniz, bu iş parçacıklarının doğrudan bir döngü çalıştıran merkezi bir iş parçacığına (oyun başına bir veya sunucu başına bir) işleyemeyecekleri istekleri iletmelerini sağlayarak doğrudan istemcilerle değil, bağlantı işleyici iplikleriyle konuşması dışında yukarıda gösterilmiştir. Bunu tek iş parçacıklı yaklaşımdan daha basit veya daha karmaşık bulmanız gerçekten size kalmış.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.