17

私はc#のtcp / ipソケットをよりよく理解しようとしています。私には、純粋に教育目的で機能するMMOインフラストラクチャ(ゲームの世界、地図、プレーヤーなど)を作成できるかどうかを確認したいと思っています。 「OMGZizは私のr0x0rMMORPGをWoWよりも優れたものにするつもりです!!!」のもう1つになるつもりです、あなたは私が話していることを知っています。

とにかく、この種のシステムの設計にどのように取り組むのか、どのようなものが必要なのか、そして何に注意すべきなのか、誰かが光を当てることができるかどうか疑問に思いました。

私の最初のアイデアは、システムを個別のクライアント/サーバー接続に分割し、各接続(独自のポート)でプレーヤー/モンスターの位置の更新、チャットメッセージの送受信などの特定のタスクを実行することでした。パケットに含まれる情報を知るためにデータにヘッダーを付ける必要がないため、データの処理が簡単になります。

それは理にかなっていて便利ですか、それとも私は物事を複雑にするだけですか?

あなたの応答は非常に高く評価されています。

4

4 に答える 4

30

ソケットレベルのプログラミングを行っている場合は、メッセージタイプごとに開くポートの数に関係なく、何らかのヘッダーが必要です。メッセージの残りの長さだけであっても。メッセージに単純なヘッダーとテールの構造を追加するのは簡単だと言っています。クライアント側のポートを1つだけ処理する方が簡単だと思います。

現代のMMORPG(そしておそらく古いものでさえ)には2つのレベルのサーバーがあったと思います。有料クライアントであることを確認するログインサーバー。確認されると、これらはすべてのゲームワールド情報を含むゲームサーバーに渡されます。それでも、クライアントが1つのソケットを開いている必要があるだけですが、それ以上のソケットを開くことはできません。

さらに、ほとんどのMMORPGは、すべてのデータも暗号化します。あなたが楽しみのための練習としてこれを書いているなら、これはそれほど重要ではありません。

一般的なプロトコルの設計/作成について、私が心配していることは次のとおりです。

エンディアン

クライアントとサーバーは常に同じエンディアンを持つことが保証されていますか?そうでない場合は、シリアル化コードでそれを処理する必要があります。エンディアンを処理する方法は複数あります。

  1. 無視する-明らかに悪い選択
  2. プロトコルのエンディアンを指定します。これは、古いプロトコルが行った/行ったことであり、したがって、常にビッグエンディアンであったネットワーク順序という用語です。どちらか一方を指定するだけで、どちらのエンディアンを指定するかは実際には重要ではありません。ビッグエンディアンを使用しない場合、一部の無愛想な古いネットワークプログラマーは腕を組むでしょうが、サーバーとほとんどのクライアントがリトルエンディアンである場合、プロトコルをビッグエンディアンにすることで余分な作業以外のものを購入することはありません。
  3. 各ヘッダーにエンディアンをマークする-クライアント/サーバーのエンディアンを通知するCookieを追加し、必要に応じて各メッセージを適宜変換することができます。残業!
  4. プロトコルにとらわれないようにする-すべてをASCII文字列として送信する場合、エンディアンは無関係ですが、はるかに非効率的です。

4つのうち通常は2つを選択し、エンディアンをクライアントの大多数のエンディアンになるように指定します。これは現在、リトルエンディアンになります。

前方互換性と後方互換性

プロトコルは下位互換性と下位互換性が必要ですか?ほとんどの場合、答えは「はい」です。この場合、これにより、バージョン管理の観点からプロトコル全体をどのように設計するか、および実際にはバージョン管理の一部であってはならない小さな変更を処理するために個々のメッセージをどのように作成するかが決まります。これをパントしてXMLを使用することはできますが、効率が大幅に低下します。

全体的なバージョン管理のために、私は通常、単純なものを設計します。クライアントは、バージョンXYを話すことを指定するバージョン管理メッセージを送信します。ただし、サーバーがそのバージョンをサポートできる限り、クライアントはクライアントのバージョンを確認するメッセージを送り返し、すべてが先に進みます。それ以外の場合は、クライアントをナックして接続を終了します。

メッセージごとに、次のようなものがあります。

+-------------------------+-------------------+-----------------+------------------------+
| Length of Msg (4 bytes) | MsgType (2 bytes) | Flags (4 bytes) | Msg (length - 6 bytes) |
+-------------------------+-------------------+-----------------+------------------------+

長さは、メッセージの長さを示しており、長さ自体は含まれていません。MsgTypeは、メッセージのタイプです。65356はアプリケーション用のメッセージタイプが豊富であるため、これには2バイトしかありません。フラグは、メッセージでシリアル化されているものを通知するためにあります。このフィールドと長さの組み合わせにより、下位互換性と下位互換性が得られます。

const uint32_t FLAG_0  = (1 << 0);
const uint32_t FLAG_1  = (1 << 1);
const uint32_t FLAG_2  = (1 << 2);
...
const uint32_t RESERVED_32 = (1 << 31);

次に、逆シリアル化コードは次のようなことを行うことができます。

uint32 length = MessageBuffer.ReadUint32();
uint32 start = MessageBuffer.CurrentOffset();
uint16 msgType = MessageBuffer.ReadUint16();
uint32 flags = MessageBuffer.ReadUint32();

if (flags & FLAG_0)
{
    // Read out whatever FLAG_0 represents.
    // Single or multiple fields
}
// ...
// read out the other flags
// ...

MessageBuffer.AdvanceToOffset(start + length);

これにより、プロトコル全体を改訂しなくても、メッセージの最後に新しいフィールドを追加できます。また、古いサーバーとクライアントが知らないフラグを無視することも保証します。新しいフラグとフィールドを使用する必要がある場合は、プロトコル全体のバージョンを変更するだけです。

フレームワークを使用するかどうか

ビジネスアプリケーションに使用することを検討するさまざまなネットワークフレームワークがあります。特にスクラッチする必要がない限り、標準のフレームワークを使用します。あなたの場合、ソケットレベルのプログラミングを学びたいので、これはすでにあなたのために答えられた質問です。

フレームワークを使用する場合は、上記の2つの懸念事項に対応していることを確認してください。または、これらの領域でフレームワークをカスタマイズする必要がある場合は、少なくとも邪魔になりません。

私は第三者と取引していますか

多くの場合、通信する必要のあるサードパーティのサーバー/クライアントを扱っている可能性があります。これは、いくつかのシナリオを意味します。

  • 彼らはすでにプロトコルを定義しています-単に彼らのプロトコルを使用してください。
  • あなたはすでにプロトコルを定義しています(そして彼らはそれを喜んで使用します)-再び簡単に定義されたプロトコルを使用します
  • 標準のフレームワーク(WSDLベースなど)を使用します-フレームワークを使用します。
  • どちらの当事者にもプロトコルが定義されていません-手元にあるすべての要因(ここで言及したすべての要因)とその能力のレベル(少なくともあなたが知る限り)に基づいて、最良の解決策を決定するようにしてください。いずれにせよ、双方がプロトコルに同意し、理解していることを確認してください。経験から、これは苦痛または楽しいことがあります。それはあなたが誰と働いているかによります。

どちらの場合も、サードパーティとは連携しないため、これは完全を期すために追加されたものです。

これについてもっともっと書けるような気がしますが、もうかなり長いです。これがお役に立てば幸いです。具体的な質問がある場合は、Stackoverflowで質問してください。

knoopxの質問に答えるための編集:

于 2008-11-13T01:04:43.133 に答える
2

歩く前、走る前にクロールする必要があると思います。最初にフレームワークと接続の設計を正しく行い、次にスケーラビリティについて心配します。あなたの目標が C# と tcp/ip プログラミングを学ぶことである場合は、これを難しくしないでください。

最初の考えを進めて、データ ストリームを分離したままにします。

楽しんで頑張ってください

于 2008-11-12T20:13:45.013 に答える
1

異なる情報に対して複数の接続を行うことはお勧めしません。処理する情報を含むヘッダーを含むプロトコルを設計します。できるだけ少ないリソースを使用します。また、さまざまな位置や統計の更新をクライアントからサーバーに送信したくない場合もあります。そうしないと、サーバーに送り返されるデータをユーザーが変更できるという状況に陥る可能性があります。

ユーザーが何かをすり抜けるためにモンスターの位置を偽造したとします。ユーザーの位置ベクトルとアクションのみを更新します。残りの情報はサーバーによって処理および検証されます。

于 2008-11-12T18:51:41.840 に答える
0

ええ、単一の接続についてはあなたが正しいと思います。もちろん、クライアントはサーバーに実際のデータを送信しません。「前進」、「左折」などの単純なコマンドのように、サーバーは移動しますマップ上のキャラクターを表示し、新しい座標をクライアントに送り返します。

于 2008-11-12T18:56:18.117 に答える