3

特定のパックされた POD 構造体をマルチキャストするアプリケーションと、他のバイナリで実行されるリスナー サービスがあります。リスナー サービスは、構造体がどのように見えるかを認識しているため、それらを受け取ると、それを a で打たれた構造体にキャストし直しreinterpret_cast、コールバックを実行します。

将来の問題は、バイナリがリリースされ、新しい情報を構造体に追加する必要がある場合、それらのバイナリを再構築する必要があるか、またはバイナリがreinterpret_cast情報を悪用したり悪用したりすることです。これは、常にそのような柔軟性を持たない実稼働環境では問題になる可能性があります。

私が言われたことの1つは、新しいスタイルのメッセージを導入し、両方を送信することです..そして、時間の経過とともに、アプリケーションは最終的に新しいタイプのバイナリメッセージに切り替えられるまで切り替えます.古いものを送るのはやめましょう。より良い代替手段があるかどうか疑問に思っていました。

たとえば、パックされた構造体の末尾にのみ新しいフィールドを追加するという規則を作成した場合、古いリッスン バイナリは、必要に応じてそれらの新しいフィールドにアクセスできる可能性があり、古い情報で構築されたバイナリは引き続き取得できる可能性があります。上部にアクセスできます。たとえば、送信者がこれをマルチキャストしている場合:

struct foo {
    int  a;
    char b[2];
} __attribute__ ((packed));

次に、いくつかのバイナリが受信側で構築され、ネットワークconst char* msg上でメッセージを取得してこれを行います。

foo* fooPtr = reinterpret_cast<foo*>(msg); 
registeredGuy->callback(fooPtr);

送信者側で追加情報をロールアウトすることを決定した場合、古いリスナーは、次のように下に追加するだけで問題ない場合があります。

struct foo {
    int  a;
    char b[2];
    char newStuff[17];
    int  k;
} __attribute ((packed));

古いレシーバーは以前の情報に正常にキャストしてアクセスできるはずですが、新しい人は新しいものにアクセスできます。これは本当ですか?また、速度に影響を与えないより良いソリューションはありますか (パフォーマンスは非常に重要です)。

4

2 に答える 2

1

本質的に同じメッセージの 2 つのバージョンを送信することは、悪い考えです。特にパフォーマンスが重要なシステムの場合。実際の送信に少なくとも 2 倍の時間を費やすことになります。また、2 倍以上の情報をブロードキャストすることになるため、ネットワークが飽和状態になる可能性があります。

メッセージ ペイロードのバージョン管理の問題は、メッセージ ペイロード自体が存在する限り存在します。私の考えでは、問題を解決する最善の方法は、問題を完全に回避することです。

重要なのは、クライアントが受信データをどのように受信して処理するかを理解することです。通常、クライアントは回線上で UDP フレームをリッスンし、それを吸い込み、そのフレームをメッセージとして処理します。理想的には、UDP フレームはアーキテクチャの MTU (たとえば 1500 バイト) よりも小さいため、メッセージが転送中に途切れることはありません。それらは順不同で到着するかもしれませんが、それはまったく別の問題です.

クライアントは、UDP フレームがどれだけ大きいかを知っています。また、処理するメッセージの大きさもわかっていますsizeof(MessageType)。彼らが知らない唯一のことは、フレームのサイズとペイロードのサイズの違いです。すべてのメッセージに固定サイズのヘッダーを含めることで、そのことを伝えることができます。

ヘッダーは次のようになります。

struct MsgHeader
{
  size_t  msg_size_;
  int  msg_type_;
  char payload_[0];
};

実際のメッセージは、これをオーバーレイするか、その直後 ( &payload_[0]) に表示されます。

クライアントは UDP フレームを読み取り、ヘッダーからフレームのサイズを取得し、その合計バイト数を 1 つのメッセージとして取得します。ペイロード ポインターから開始して、クライアントはインバウンド データを問題のメッセージ タイプとしてキャストします。1 つのメッセージに、クライアントが理解できるメッセージ タイプに収まらないデータが含まれている場合、クライアントはそれを無視して破棄します。

メッセージに変更を加えるときは、既存のフィールドの場所を移動しないことで、下位バイナリ互換性を確保する必要があります。最後に新しいフィールドを追加し、それに応じてヘッダーのフレーム サイズを大きくします。Bob はあなたのおじです。

于 2012-11-16T20:05:05.093 に答える
0

通常、元の構造体を変更してフィールドを追加するのではなく、最初に元の構造体と同じフィールドを持つ新しい構造体を定義します。古いクライアントが構造体の新しいバージョンのバイナリ形式を安全に処理できるようにするには、インスタンスのサイズを最初のバージョンのフィールドとして含める必要があります。

各インスタンスにサイズを含めることで、将来的に foo を fooex に拡張することができ、古いクライアントは foo と fooex の混合で構成される一連の要素を安全に処理できます。それらはfooのサイズです。

例えば:

struct foo {
 size_t size;
 int  a;
 char b[2];
} __attribute__ ((packed));

struct fooex {
 size_t size;
 int  a;
 char b[2];
 char newStuff[17];
 int  k;
} __attribute ((packed));
于 2012-11-16T19:46:59.713 に答える