0

Linux上のCで単純なサーバー/クライアントを実装しています。クライアントとして、サーバーに特定のファイルがあるかどうかをサーバーに確認したいとします。構造体内のサーバーにクエリを送信できるようにしたい(このアクションの問題には精通しているが、宿題用である)。

私の問題は、構造体を使用するときに、ファイル名にメモリを事前に割り当てることができないことです。したがって、次の形式の構造体:

struct{
int operation;
char *fileName;
long fileSize;
};

fileNameにメモリを割り当ててもsizeof(struct)同じまま(sizeof(char*)+sizeof(fileSize))なので、サーバーに送信できません。

ここで明確になっていることを願っています...私の質問は、構造体を使用して、サイズが不明な文字列をサーバーに送信するにはどうすればよいかということです。(1つのオプションは、fileSizeを詳細に送信してから、そのサイズのchar *を送信することですが、私はしたくありません)。

ありがとう!

4

5 に答える 5

2

私があなたの問題を理解したかどうかわかりません。しかし、構造体を引き続き使用し、最後にファイル名をパックする方法があります。ここで重要なのは、構造体 + 構造体の後に隠されるファイル名データに十分なバイト数を malloc する必要があるということです。

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct
{
    // put any data you want here
    // ...
    size_t fileNameLength;
    char fileName[0]; // fake array, used to access extra bytes allocated after the struct
} FileInfo;

int main (int argc, char **argv)
{
    FileInfo *fi = NULL;

    // The fake array has size 0 !
    printf ("sizeof FileInfo is %d\n", sizeof (FileInfo));

    // let's suppose you've guessed your filename
    char filename[] = "homework.txt";
    size_t filename_length = strlen (filename); // this is the length with no NULL byte
    printf ("filename length (without NULL byte) is %d\n", filename_length);

    fi = malloc (sizeof(FileInfo) + filename_length);
    fi->fileNameLength = filename_length;
    memcpy (&fi->fileName, filename, filename_length);

    printf ("filename is %.*s\n", fi->fileNameLength, fi->fileName);

    // To send your struct + filename, just use the same address (fi),
    // but don't use as length sizeof (FileInfo), but this length:
    size_t struct_total_length = sizeof (FileInfo) + fi->fileNameLength;
    return 0;
}
于 2012-11-08T18:19:01.883 に答える
1

送信を 3 つのコマンド (操作 (int)、ファイル名の長さ (int)、ファイル名 (0..n 文字)) に分割できます。

typedef struct Cmd{
    int operation;
    char *fileName;
    long fileSize; /* not required here but was in the question */
} Cmd;

Cmd cmd;
...
send(sockfd, &cmd.operation, sizeof(cmd.operation), 0);
size_t len = strlen(cmd.fileName);
send(sockfd, &len, sizeof(len), 0);
send(sockfd, fileName, len, 0);

宿題で単一の送信コマンドを使用する必要がある場合は、3 つすべてに十分な大きさのバッファーを割り当ててから、そこにコピーします。

于 2012-11-08T15:34:43.863 に答える
1

あるマシンから別のマシンにワイヤを介して構造を送信したくないだけでなく、潜在的なエンディアンの問題もあります。また、あるマシンの構造レイアウトが別のプラットフォームの構造レイアウトと同じではない可能性があるという問題に直面する可能性があります.

あなたが望むものは:

  Client side                                     Server side

構造体をデータストリームに変換 ---(1)----> データストリームを構造体に変換

次に、データ ストリームの構文のコントラクト (1) を定義するだけです。

codewarrior が言うように、最大​​のファイル名を配線することはめったに良い考えではありません。

于 2012-11-08T16:55:06.277 に答える
0

あなたの場合、最初にファイルの長さを送信し、次にその名前を送信するよりも良い解決策はないと思います。

以下のように構造体を使用することもできます(事前送信サイズなし)

#define LONG_ENOUGH_FOR_FILE_NAME 128 //this defined to what you need
struct{
  int operation;
  char fileName[LONG_ENOUGH_FOR_FILE_NAME];
  long fileSize;
};

しかし、これは非常に脆弱なソリューションです。あなたはより長いファイル名を持つことができます

LONG_ENOUGH_FOR_FILE_NAME

バッファサイズとこれはいくつかの深刻な問題につながります。

于 2012-11-08T15:38:33.853 に答える
0

データを「シリアル化」する必要があります。長さをエンコードし、長さを書き込み、文字列をエンコードし (これは通常ノーオペレーションです)、文字列を書き込みます。

送信者:

uint32_t net_length = htonl(strlen(filename)+1);  // include trailing NUL
send(socket, &net_length, 4);
send(socket, filename, strlen(filename));

レシーバー (疑似コード):

char buffer[whatever]
until have at least 4 bytes in buffer {
  got = recv(socket, buffer+have, sizeof(buffer)-have);
  have += got
}
uint32_t length = ntohl(*(uint32_t*)buffer)
until have at least length+4 bytes in buffer {
  got = recv(socket, buffer+have, sizeof(buffer)-have);
  have += got
}
char* filename = &buffer[4]

これは理想的ではない固定サイズの受信バッファーを使用しますが、アイデアを示しています。malloc()長さ (4 バイト) になるまで読み込んでから、ファイル名を保持するのに十分な大きさのバッファを取得し、残りをそこに直接読み込む方がよいでしょう。

于 2012-11-08T19:13:18.183 に答える