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