0

我正在 Linux 上用 C 语言实现一个简单的服务器/客户端。可以说,作为客户端,我想询问服务器是否有特定文件。我希望能够将我的查询发送到结构内的服务器(我更熟悉此操作的问题,但它是家庭作业)。

我的问题是,在使用结构时,我无法为文件名预先分配内存。所以形式的结构:

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

对我没有帮助,因为即使我为 fileName 分配内存,它也sizeof(struct)保持不变,(sizeof(char*)+sizeof(fileSize))所以我无法将它发送到服务器。

希望我在这里很清楚......我的问题是如何使用 struct 向服务器发送一个未知大小的字符串。(一种选择是提前发送 fileSize,然后发送该大小的 char*,但我不想这样做)。

谢谢!

4

5 回答 5

2

不确定我是否理解您的问题。但这是一种仍然使用您的结构并在最后打包文件名的方法。这里重要的是您需要为结构分配足够的字节+结构之后隐藏的文件名数据。

#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);

如果您的作业要求您使用单个发送命令,请为所有三个分配一个足够大的缓冲区,然后复制到其中。

于 2012-11-08T15:34:43.863 回答
1

您不想将结构从一台机器发送到另一台机器以及潜在的字节序问题,您还可能面临这样的问题:一台机器上的结构布局可能与不同平台上的结构布局不同.

你想要的是:

  Client side                                     Server side

将 struct 转换为数据流 ---(1)----> 将数据流转换为 struct

然后,您只需为数据流的语法定义合同 (1) 定义文件名前面有它们的长度似乎是合理的,或者发送文件名 null 终止。

正如codewarrior所说,硬连线最大文件名很少是一个好主意。

于 2012-11-08T16:55:06.277 回答
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]

这使用了一个不理想的固定大小的接收缓冲区,但它说明了这个想法。你最好只阅读直到你有长度(4个字节),然后malloc()用来获得一个足够大的缓冲区来保存文件名并将其余部分直接读入其中。

于 2012-11-08T19:13:18.183 回答
0

我认为在您的情况下,没有比首先发送文件长度然后发送其名称更好的解决方案了。

您还可以使用 struct 如下(不预先发送大小)

#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 回答