繁体   English   中英

一种通过TCP发送结构中未知长度字符串的聪明方法? (C,Linux)

[英]A smart way to send unknown length string in a struct over TCP? (C, Linux)

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

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

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

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

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

谢谢!

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

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

您可以将发送分为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);

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

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

您想要的是:

  Client side                                     Server side

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

然后,您只需要为数据流的语法定义协定(1),就可以定义文件名以其长度开头似乎是合理的,或者发送文件名null终止。

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

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

您也可以按以下方式使用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

缓冲区大小,这将导致一些严重的问题。

您需要“序列化”数据。 编码长度,写入长度,编码字符串(通常是无操作),然后写入字符串。

发件人:

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()获得足够大的缓冲区来容纳文件名,然后直接将其余部分读入该文件中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM