ソケットを介してn個のクライアントと通信するターンベースのサーバーを記述するパターンはありますか?


14

私は、ゲームをプレイするTCPソケットネットワーククライアントの任意の数のゲームを管理する汎用ゲームサーバーで作業しています。ダクトテープでハッキングされた「デザイン」が動作していますが、壊れやすく柔軟性に欠けています。堅牢で柔軟性のあるクライアント/サーバー通信を作成するための確立されたパターンはありますか?(そうでない場合、私が下にあるものをどのように改善しますか?)

大体これがあります:

  • ゲームのセットアップ中、サーバーには、クライアントからの同期リクエストとサーバーからの応答を処理するプレーヤーソケットごとに1つのスレッドがあります。
  • ただし、ゲームが開始されると、1つのスリープを除くすべてのスレッドと、そのスレッドが(逆の要求と応答で)移動について通信しながらすべてのプレイヤーを1つずつ循環します。

ここに私が現在持っているものの図があります。クリックして拡大/読み取り可能なバージョン、または66kB PDFをクリックします。

フローのシーケンス図

問題点:

  • プレイヤーは、正確に正しいメッセージで順番に応答する必要があります。(私は各プレイヤーにランダムながらくたに応答させ、彼らが有効な動きを与えてから先に進むことができると思います。)
  • 順番が来ない限り、プレイヤーがサーバーと会話することはできません。(サーバーに他のプレーヤーに関する更新を送信するように指示することはできますが、非同期要求は処理しません。)

最終要件:

  • パフォーマンスは最優先事項ではありません。これは、主に非リアルタイムゲームに使用され、主にAIを相互にピッチングするために使用され、けいれん的な人間には使用されません。

  • ゲームプレイは常に(非常に高い解像度であっても)ターンベースになります。他のすべてのプレイヤーがターンを取得する前に、各プレイヤーは常に1つの動きを処理します。

サーバーの実装はたまたまRubyで行われます(違いがある場合)。


クライアントごとにTCPソケットを使用する場合、この上限は(65535-1024)クライアントになりませんか?
o0 '。

1
なぜそれが問題なのですか、ロホリス?ほとんどの人は6000人の同時ユーザーを獲得するのに苦労します
。60000を

@Kylotan確かに。10個以上の同時AIがある場合、驚かれることでしょう。:)
Phrogz

好奇心から、その図を作成するためにどのツールを使用しましたか?
マティアス14年

@matthias残念ながら、Adobe Illustratorでのカスタム作業は多すぎました。
プログズ14年

回答:


10

正確に何を達成したいのかわかりません。しかし、ゲームサーバーで常に使用されている1つのパターンがあります。メッセージキューを使用します。

具体的には、クライアントがサーバーにメッセージを送信するとき、すぐにメッセージを処理しないでください。むしろ、それらを解析し、この特定のクライアントのキューに入れます。次に、いくつかのメインループ(別のスレッドでも)ですべてのクライアントを順番に調べ、キューからメッセージを取得して処理します。このクライアントのターンが終了したことが処理で示されたら、次へ進みます。

このように、クライアントは厳密に順番に作業する必要はありません。クライアントが処理されるまでに何かをキューに入れるのに十分な速さだけです(もちろん、クライアントを待つか、遅れている場合は順番をスキップできます)。また、「非同期」キューを追加することにより、非同期リクエストのサポートを追加できます。クライアントが特別なリクエストを送信すると、この特別なキューに追加されます。このキューは、クライアントのキューよりも頻繁にチェックおよび処理されます。


1

ハードウェアスレッドは、3桁のプレーヤーに対して「プレーヤーごとに1つ」を合理的なアイデアとするほど十分にスケーリングされず、いつ起動するかを知るロジックは複雑になります。より良いアイデアは、読み取りまたは書き込み操作の実行中にプログラムスレッド全体を一時停止することなくデータを送受信できるRubyの非同期I / Oパッケージを見つけることです。また、読み取り操作でスレッドがハングすることがないため、プレーヤーの応答を待つ問題も解決します。代わりに、サーバーは時間制限が切れたかどうかを確認し、それに応じて他のプレイヤーに通知することができます。

基本的に「非同期I / O」は探している「パターン」ですが、実際にはパターンではなく、より多くのアプローチです。ソケットで明示的に「読み取り」を呼び出し、データが到着するまでプログラムを一時停止する代わりに、データの準備ができたらいつでも「onRead」ハンドラーを呼び出すようにシステムをセットアップし、それまで処理を続けます。

各ソケットには順番があります

各プレーヤーにはターンがあり、各プレーヤーにはデータを送信するソケットがありますが、これは少し異なります。ある日、プレーヤーごとに1つのソケットが必要ない場合があります。ソケットをまったく使用しない場合があります。責任の領域を分離してください。これは重要でない詳細のように聞こえますが、異なる設計のコンセプトを統合すると、より良いアプローチを見つけて議論することが難しくなります。


1

確かに複数の方法がありますが、個人的には、個別のスレッドを完全にスキップし、イベントループを使用します。これを行う方法は、使用しているI / Oライブラリによって多少異なりますが、基本的に、メインサーバーループは次のようになります。

  1. 接続プールと新しい接続用の待機ソケットを設定します。
  2. 何かが起こるのを待ちます。
  3. 何かが新しい接続である場合、プールに追加します。
  4. 何かがクライアントからのリクエストである場合、すぐに処理できるものかどうかを確認します。はいの場合、そうします。そうでない場合は、キューに入れて(オプションで)クライアントに確認応答を送信します。
  5. また、キューに何か処理できるものがあるかどうかも確認してください。もしそうなら、それを行います。
  6. 手順2に戻ります。

たとえば、ゲームに参加しているクライアントがn人いるとします。その後、最初のn-1が移動を送信すると、移動が有効に見えることを確認し、移動が受信されたが他のプレイヤーが移動するのを待っているというメッセージを送り返します。n人のプレーヤー全員が移動した後、保存したすべての移動を処理し、すべてのプレーヤーに結果を送信します。

このスキームを改良してタイムアウトを含めることもできます。ほとんどのI / Oライブラリには、新しいデータが到着する、一定の時間が経過するまで待機するメカニズムが必要です。

もちろん、接続ごとに個別のスレッドでこのようなものを実装することもできます。これらのスレッドは、ループを実行する中央のスレッド(ゲームごとまたはサーバーごとに1つ)に直接処理できない要求を渡すことで上記と同じですが、クライアントと直接通信するのではなく、接続ハンドラスレッドと通信する点が異なります。シングルスレッドアプローチよりもこの単純な方法と複雑な方法のどちらを見つけるかは、あなた次第です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.