[英]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.