0

通信用にバイトベースの接続指向プロトコルに依存する 2 つの Java プロジェクト (単純なマルチプレイヤー ゲーム) がありました。

どちらの場合も、私は通信の実装に不満を持っていました。なぜなら、インテリジェントで、冗長でなく、オブジェクト指向の書き込み方法、特にバイトの解析方法を思いつくことができなかったからです。


書くために、私は次のようなものを持っていました

ProtocolDataUnitX pdux = new ProtocolDataUnitX("MyName", 2013);
int[] bytes = pdux.getBytes();
out.write(bytes); // surrounded with try/catch etc.

AbstractPDUいくつかのバイト変換の便利なメソッドを持つクラスがあったので、それはある程度受け入れられました。getBytes()しかし、プロトコル データ ユニット (pdu) ごとにメソッドを定義する必要がありました。着信バイト ストリームを解析するための私のアプローチには、それ以上の革新がありませんでした。

private InputStream in;

...

@Override
public void run() {

    int c;
    while ((c = in.read()) != -1)) {
                if (c == 0x01) {
                    // 0x01 means we have pdu #1 and can continue reading
                    // since we know what is coming.
                    // after we have all bytes and know the pdu
                    // we can determine the paramters. I.e., every pdu has a
                    // reverse constructor: bytes -> pdu
                }

質問

これらの状況をどのように処理しますか? ここでのベストプラクティスは何ですか? 一部のプロトコルでは、全長フィールドがエンコードされていますが、エンコードされていないプロトコルもあります。一部のプロトコル データ ユニットは可変長です。ここに合理的なアプローチはありますか?たぶん、ある種のスキーマ定義ですか?この件に関して、これ以上醜くて紛らわしいコードを作成したくありません。

4

1 に答える 1

3

概要:ベスト プラクティスは、既存の成熟したプロトコル コンパイラを使用することです。Google protobufs は一般的な選択肢です。


長年にわたり、多くのプロトコル定義システムが開発されてきました。これらのほとんどには、プロトコルの記述を取り、多くの場合複数の言語でクライアントとサーバーのコードを生成するコンパイラが含まれています。このようなコンパイラの存在は、他のチームが標準の PDU 定義を使用して独自のクライアントまたはサーバーを簡単に作成できるため、単一のクライアント (またはサーバー) 実装に制限されていないプロジェクトで非常に役立ちます。また、お気づきのように、必要な機能のほとんどを備えた Java のような言語であっても、クリーンなオブジェクト指向インターフェースを作成することは簡単ではありません。

PDU に明示的な長さを持たせるか、それとも自己区切りにする (たとえば、終了標識を使用する) べきかという問題は興味深いものです。明示的な長さには多くの利点があります。1 つは、PDU を受け入れるために完全なパーサーを用意する必要がないことです。これにより、デシリアライゼーションを送信からより適切に分離できます。伝送が PDU のストリームで構成される場合、明示的な長さフィールドによってエラー回復が簡単になり、PDU をハンドラーに早期にディスパッチできます。また、明示的な長さフィールドを使用すると、PDU を別の PDU 内に埋め込むことが容易になります。これは、特に PDU の一部を暗号化する必要がある場合に役立ちます。

一方、明示的な長さフィールドでは、送信前に PDU 全体をメモリ内で組み立てる必要があります。これは、大きな PDU では扱いにくく、単一の PDU でストリーミングすることは不可能です。長さフィールド自体が可変長である場合 (ほとんどの場合必要です)、最初に最終的な長さが分からない限り、PDU コンポーネントを作成するのは困難です。(この問題の解決策の 1 つは、シリアル化された文字列をbackwardsで作成することですが、これも扱いにくく、ストリーミングには適していません。)

全体として、バランスは明示的な長さフィールドを支持していますが、一部のシステムでは「チャンク」が許可されています。チャンキングの単純な形式は、最大チャンク サイズを定義し、最大サイズの連続するチャンクと、最大サイズより小さいサイズの最初の後続チャンクを連結することです。(PDU が最大サイズの偶数倍である場合に備えて、長さ 0 のチャンクを指定できることが重要です。) これは妥当な妥協点です。ストリーミングが可能です(多少の作業が必要です)。しかし、それにはより多くのエンジニアリング作業が必要であり、テストとデバッグが必要な多くのコーナー ケースが作成されます。

PDU フォーマットを設計する際の重要な原則の 1 つは、すべてのオプションが潜在的な情報漏洩の可能性があるということです。可能な範囲で、特定の内部オブジェクトが可能なシリアル化を 1 つだけ持つようにしてください。また、冗長性にはコストがかかることを覚えておいてください。重複がある場合はどこでも、有効性のテストを意味します。テストを最小限に抑えることは、特にデシリアライゼーションの効率化の鍵です。有効性テストをスキップすることは、セキュリティ攻撃への誘いです。

私の意見では、アドホック プロトコル パーサーを作成することは、通常は良い考えではありません。一つには、それは多くの仕事です。もう一つは、微妙な問題がたくさんあり、それらに対処したシステムを使用する方が良い.

個人的には、特に電気通信業界で広く使用されている ASN.1 のファンですが、小さなプロジェクトに適合させるのは簡単なテクノロジではありません。学習曲線は非常に急で、好きなオープンソース ツールはそれほど多くありません。

現在、おそらく最も人気のあるオプションはGoogle protobufs です。これは、C++、Java、および Python (および提供されたプラグインを介して他の多くの言語) で利用できます。シンプルで、かなり使いやすく、オープンソースです。

于 2013-07-10T17:02:06.140 に答える