簡體   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