9

いくつかのプリミティブ データ型、ポインター、および構造体ポインターで構成される構造体オブジェクトがあります。もう一方の端で使用できるように、ソケット経由で送信したい。シリアル化コストを前払いしたいので、その構造体のオブジェクトを初期化して、マーシャリングせずにすぐに送信できるようにするにはどうすればよいですか? 例えば

struct A {
    int i;  
    struct B *p;
};

struct B {
    long l;
    char *s[0];
};

struct A *obj; 

// can do I initialize obj?
int len = sizeof(struct A) + sizeof(struct B) + sizeof(?);
obj = (struct A *) malloc(len);
...

write(socket, obj, len);

// on the receiver end, I want to do this
char buf[len];

read(socket, buf, len);
struct A *obj = (struct A *)buf;
int i = obj->i;
char *s = obj->p->s[0];
int i obj.i=1; obj.p.

ありがとうございました。

4

6 に答える 6

5

これを行う最も簡単な方法は、すべてを保持するためにメモリのチャンクを割り当てることです。たとえば、構造体を次のように考えます。

typedef struct A {
  int v;
  char* str;
} our_struct_t;

これを行う最も簡単な方法は、定義済みのフォーマットを作成し、それをバイト配列にパックすることです。例を示します。

int sLen = 0;
int tLen = 0;
char* serialized = 0;
char* metadata = 0;
char* xval = 0;
char* xstr = 0;
our_struct_t x;
x.v   = 10;
x.str = "Our String";
sLen  = strlen(x.str); // Assuming null-terminated (which ours is)
tLen  = sizeof(int) + sLen; // Our struct has an int and a string - we want the whole string not a mem addr
serialized = malloc(sizeof(char) * (tLen + sizeof(int)); // We have an additional sizeof(int) for metadata - this will hold our string length
metadata = serialized;
xval = serialized + sizeof(int);
xstr = xval + sizeof(int);
*((int*)metadata) = sLen; // Pack our metadata
*((int*)xval) = x.v; // Our "v" value (1 int)
strncpy(xstr, x.str, sLen); // A full copy of our string

したがって、この例では、データをサイズの配列にコピーします2 * sizeof(int) + sLen。これにより、単一の整数のメタデータ (つまり、文字列の長さ) と構造体から抽出された値が可能になります。逆シリアル化するには、次のように想像できます。

char* serialized = // Assume we have this
char* metadata = serialized;
char* yval = metadata + sizeof(int);
char* ystr = yval + sizeof(int);
our_struct_t y;
int sLen = *((int*)metadata);
y.v = *((int*)yval);
y.str = malloc((sLen + 1) * sizeof(char)); // +1 to null-terminate
strncpy(y.str, ystr, sLen);
y.str[sLen] = '\0';

ご覧のとおり、バイト配列は明確に定義されています。以下に、構造の詳細を示します。

  • バイト 0-3 : メタデータ (文字列の長さ)
  • バイト 4 ~ 7 : Xv (値)
  • バイト 8 - sLen : X.str (値)

この種の明確に定義された構造により、定義された規則に従えば、任意の環境で構造体を再作成できます。この構造体をソケット経由で送信するかどうかは、プロトコルの開発方法によって異なります。最初に、作成したばかりのパケットの全長を含む整数パケットを送信するか、メタデータが最初に/個別に送信されることを期待できます (論理的には個別に、これは技術的にはすべて同時に送信できます)。クライアント側で受信するデータの量を知っています。たとえば、メタデータ値を受け取った場合、構造体を完了するためにバイトが続く10ことが期待できます。sizeof(int) + 10一般に、これはおそらく14バイトです。

編集

コメントで要求されたいくつかの説明をリストします。

(論理的に) 連続したメモリにあるように、文字列の完全なコピーを作成します。つまり、シリアル化されたパケット内のすべてのデータは、実際には完全なデータであり、ポインターはありません。serializedこのようにして、ソケットを介して単一のバッファ ( is と呼びます) を送信できます。単純にポインターを送信した場合、ポインターを受信したユーザーは、そのポインターが有効なメモリ アドレスであることを期待します。ただし、メモリ アドレスがまったく同じになる可能性はほとんどありません。ただし、たとえそうであったとしても、彼はそのアドレスであなたと同じデータを持っているわけではありません (非常に限られた特殊な状況を除いて)。

この点は、デシリアライゼーション プロセス (これは受信者側にあります) を見ることでより明確になることを願っています。送信者から送信された情報を保持する構造体をどのように割り当てているかに注目してください。送信者が完全な文字列ではなくメモリ アドレスのみを送信した場合、送信されたデータを実際に再構築できませんでした (同じマシン上でも、同じではない 2 つの異なる仮想メモリ スペースがあります)。したがって、本質的に、ポインターはオリジネーターにとって適切なマッピングにすぎません。

最後に、「構造体内の構造体」に関する限り、構造体ごとにいくつかの関数が必要になります。とはいえ、関数を再利用できる可能性はあります。たとえば、2 つの構造体がAあり、Bwhere がA含まれている場合、 B2 つのシリアル化メソッドを使用できます。

char* serializeB()
{
  // ... Do serialization
}

char* serializeA()
{
  char* B = serializeB();
  // ... Either add on to serialized version of B or do some other modifications to combine the structures
}

したがって、構造体ごとに 1 つのシリアル化メソッドで問題を解決できるはずです。

于 2013-03-29T17:29:46.173 に答える
4

この答えは、あなたの問題のほかにありますmalloc

残念ながら、標準と互換性のある優れたトリックは見つかりません。構造体を適切にシリアル化する唯一の方法は、各要素を個別にバイトに分割し、それらを unsigned char 配列に書き込み、それらをネットワーク経由で送信し、断片を元に戻すことです。つまり、多くのシフト操作とビット単位の操作が必要になります。

場合によっては、ある種のプロトコルを定義する必要があります。たとえば、あなたの場合、オブジェクトpが の直後を指していることを常に確認する必要があるstruct Aため、回復したら、ポインターを適切に設定できます。ネットワークを介してポインタを送信できないことは、すでに十分に説明されていますか?

あなたがやりたいかもしれないもう一つのプロトコル的なことは、柔軟な配列メンバーsに割り当てられたサイズを に書き込むことですstruct B。選択したシリアル化されたデータのレイアウトが何であれ、明らかに両側が尊重する必要があります。

バイトの順序、構造パディング、基本型のサイズなど、マシン固有のものに依存できないことに注意することが重要です。これは、要素の各フィールドを個別にシリアル化し、固定バイト数を割り当てる必要があることを意味します。

于 2013-03-29T17:17:25.767 に答える
1

プラットフォームに依存しない方法でデータをシリアル化する必要があります。

Binnライブラリ (私の作成)を使用した例を次に示します。

  binn *obj;

  // create a new object
  obj = binn_object();

  // add values to it
  binn_object_set_int32(obj, "id", 123);
  binn_object_set_str(obj, "name", "Samsung Galaxy Charger");
  binn_object_set_double(obj, "price", 12.50);
  binn_object_set_blob(obj, "picture", picptr, piclen);

  // send over the network
  send(sock, binn_ptr(obj), binn_size(obj));

  // release the buffer
  binn_free(obj);

文字列をキーとして使用したくない場合は、整数をキーとして使用する binn_map を使用できます。リストのサポートもあります。また、構造を別の構造内に挿入することもできます (ネストされた構造)。例えば:

  binn *list;

  // create a new list
  list = binn_list();

  // add values to it
  binn_list_add_int32(list, 123);
  binn_list_add_double(list, 2.50);

  // add the list to the object
  binn_object_set_list(obj, "items", list);

  // or add the object to the list
  binn_list_add_object(list, obj);
于 2016-11-26T03:45:17.123 に答える
0

データを解釈し、シリアル化する対象を理解します。整数と B 型の構造体をシリアル化したい (再帰的に、int、long、および文字列の配列をシリアル化したい)。次に、それらをシリアル化します。必要な長さ sizeof(int) + sizeof(long) + ∑strlen(s[i])+1.

一方、シリアル化は解決済みの問題です(実際には複数回)。シリアル化ルーチンを手書きする必要がありますか? D-Bus や単純な RPC 呼び出しを使用しないのはなぜですか? ご利用をご検討ください。

于 2013-03-29T17:40:49.697 に答える
-1

@Shahbazは正しいです私はあなたが実際にこれを望んでいると思います

int len = sizeof(struct A);
obj = (struct A *) malloc(len);

しかし、ポインターが指すアドレスは他のマシンでは意味がないため、ポインターを別のマシンに送信するときにも問題が発生します。

于 2013-03-29T17:10:06.693 に答える