16

私は多くのネットワークシステムを作成し、ネットワークがどのように機能するかについて良い考えを持っています。しかし、私はいつも巨大なswitchステートメントであるパケット受信機能を持っていることになります。これは私に届き始めています。私は、受信パケットを処理するための優れたエレガントなオブジェクト指向の方法をはるかに望んでいますが、良い解決策を考え出すたびに、私はいつも不足してしまいます。

たとえば、ネットワークサーバーがあるとします。ただそこで応答を待っているだけです。パケットが着信し、サーバーはパケットを検証してから、パケットの処理方法を決定する必要があります。

現時点では、ヘッダーのパケットIDをオンにしてから、各パケットタイプを処理する大量の関数呼び出しを使用してこれを行っています。複雑なネットワークシステムでは、これはモノリシックなswitchステートメントになり、私はそれをこのように扱うのが本当に好きではありません。私が検討した1つの方法は、ハンドラークラスのマップを使用することです。次に、パケットを関連するクラスに渡し、着信データを処理できます。これに関して私が抱えている問題は、各パケットハンドラーをマップに「登録」する方法が必要なことです。つまり、通常、クラスの静的コピーを作成してから、コンストラクターで中央のパケットハンドラーに登録する必要があります。これは機能しますが、実際にはそれを処理するためのエレガントで厄介な方法のように見えます。

編集:同様に、両方の方法で機能する優れたシステムがあれば理想的です。つまり、同じパケットタイプの送信を(明らかに異なる関数を介して)受信するのと同じように簡単に処理できるクラス構造です。

誰かが私に着信パケットを処理するためのより良い方法を教えてもらえますか?リンクや役立つ情報をいただければ幸いです。

問題をうまく説明できなかった場合や、問題をうまく説明できなかった場合の謝罪も、解決策を思い付くことができなかった理由です。

4

7 に答える 7

5

パケットタイプの処理方法について:私にとってはマップが最適です。ただし、マップの代わりにプレーン配列(またはベクトル)を使用します。パケットタイプを0から順番に列挙すると、アクセス時間が一定になります。

クラス構成について。すでにこの仕事をしているライブラリがあります:利用可能なゲームネットワークプロトコル定義言語とコード生成。たとえば、GoogleのProtocolBufferは有望なようです。プロトコル記述内のすべてのメッセージに対して、ゲッター、セッター、シリアル化および逆シリアル化ルーチンを備えたストレージクラスを生成します。プロトコル記述言語は多かれ少なかれ豊富に見えます。

于 2011-02-08T20:57:16.587 に答える
1

ハンドラーインスタンスのマップは、それを処理するためのほとんど最良の方法です。それについてエレガントなものは何もありません。

于 2011-02-08T19:20:24.923 に答える
1

私の経験では、テーブル駆動型の解析が最も効率的な方法です。

std::mapいいのですが、結局静的テーブルを使うことになります。定数テーブルとして静的に初期化することはstd::mapできません。実行時にロードする必要があります。テーブル(構造体の配列)は、データとして宣言し、コンパイル時に初期化できます。線形探索がボトルネックであったほど大きなテーブルに遭遇したことはありません。通常、テーブルサイズは十分に小さいため、バイナリ検索のオーバーヘッドは線形検索よりも遅くなります。

パフォーマンスを高めるために、メッセージデータをテーブルへのインデックスとして使用します。

于 2011-02-08T20:13:06.993 に答える
1

OOPを行うときは、すべてのものをオブジェクトとして表現しようとしますよね?したがって、プロトコルメッセージもオブジェクトになります。おそらくYourProtocolMessageBase、メッセージの動作をカプセル化し、そこから多態的に特殊化されたメッセージを継承する基本クラスがあります。次に、すべてのメッセージ(つまりすべてYourProtocolMessageBaseのインスタンス)をバイトの文字列に変換する方法と、その逆を行う方法が必要です。このような方法は、シリアル化手法と呼ばれます。いくつかのメタプログラミングベースの実装が存在します。

Pythonの簡単な例:

from socket import *
sock = socket(AF_INET6, SOCK_STREAM)
sock.bind(("localhost", 1234))
rsock, addr = sock.accept()

サーバーがブロックし、クライアントの別のインスタンスを起動します。

from socket import *
clientsock = socket(AF_INET6, SOCK_STREAM)
clientsock.connect(("localhost", 1234))

次に、Pythonの組み込みシリアル化モジュールを使用しますpickle。クライアント:

import pickle
obj = {1: "test", 2: 138, 3: ("foo", "bar")}
clientsock.send(pickle.dumps(obj))

サーバ:

>>> import pickle
>>> r = pickle.loads(rsock.recv(1000))
>>> r
{1: 'test', 2: 138, 3: ('foo', 'bar')}

ご覧のとおり、Pythonオブジェクトをリンクローカルで送信しました。これはOOPではありませんか?

シリアル化の唯一の可能な代替手段は、バイマップID⇔クラスを維持することだと思います。これは本当に避けられないように見えます。

于 2011-02-08T20:13:48.173 に答える
1

同じパケットネットワークプロトコルを使い続けたいのですが、それをプログラミングのオブジェクトに変換しますよね?

データをプログラミングオブジェクトとして扱うことができるプロトコルはいくつかありますが、アプリケーションでの扱いと同じように、プロトコルを変更したくないようです。

パケットには、特定のオブジェクトクラスにマップできる「タグ」やメタデータ、または「ID」や「データ型」などが含まれていますか?含まれている場合は、IDを格納する配列を作成できます。および一致するクラス、およびオブジェクトを生成します。

于 2011-02-08T21:09:48.873 に答える
1

これを処理するためのよりOOな方法は、状態パターンを使用してステートマシンを構築することです。

着信生データの処理は、ステートマシンがエレガントなソリューションを提供する場合の解析です(エレガントとパフォーマンスのどちらかを選択する必要があります)

処理するデータバッファーがあります。各状態には、バッファーの自分の部分を解析して処理し(すでに可能な場合)、コンテンツに基づいて次の状態を設定するハンドルバッファーメソッドがあります。

パフォーマンスを求めている場合でも、ステートマシンを使用できますが、OOの部分は省略してください。

于 2011-02-10T10:50:37.633 に答える
1

FlatbuffersCap'nProtoコードジェネレーターを使用します。

于 2019-04-25T07:09:34.063 に答える