【问题标题】:C/C++ Downloading / Uploading Files via socketC/C++ 通过套接字下载/上传文件
【发布时间】:2022-02-21 21:34:52
【问题描述】:

我目前正在尝试通过套接字 (SOCK_STREAM) 下载/上传文件。以下两个函数是我用来发送和接收数据的。 我目前遇到以下问题: 结果文件有时与源文件的大小不同 这个问题越严重,文件越大。

我很确定我遗漏了一些明显的东西,可能是在我的循环中或确定数据流的结尾。过去一周我尝试了一堆不同的方法/解决方案,这是最接近我得到的工作版本...

感谢您的任何建议和评论,如果我需要提供更多信息,请告诉我

从服务器向客户端发送数据的函数:

void send_file(char *filename, int sock)
{
    char data[1024] = {0};
    FILE *fp = fopen(filename, "rb");

    while (fread(data, sizeof(char), sizeof(data), fp) == 1024) {
        if (send(sock, data, sizeof(data), 0) == -1) {
            printf("%s%s[-] Error Transmitting File\n\n", KRED, BGHT);
            break;
        }
        bzero(data, sizeof(data));
    }
    bzero(data, sizeof(data));
    strcpy(data, "!EOF!");
    send(sock, data, sizeof(data), 0);
    bzero(data, sizeof(data));
    printf("%s%s[+] Upload Successful\n\n", KGRN, BGHT);
    fclose(fp);
}

客户端从服务器接收数据的功能:

void write_file(int sock, char *filepath)
{
    FILE *fp;
    int n;
    char *lastSlash = strrchr(filepath, '\\');
    char *filename = lastSlash ? lastSlash +1 : filepath;
    char data[1024] = {0};
    fp = fopen(filename, "wb");

    while (1) {
        n = recv(sock, data, sizeof(data), 0);
        if (strncmp("!EOF!", data, 5) == 0) {
            break;
        }
        if (n <= 0) {
            break;
            return;
        }
        fwrite(data, sizeof(char), sizeof(data), fp);
        bzero(data, sizeof(data));
    }
    fclose(fp);
    return;
}

【问题讨论】:

  • 在使用sendrecv 返回的值时,您需要更加小心。您将recv 返回的值存储在n 中,但始终将sizeof(data) 字符写入目标文件。
  • 此代码中的幅度检查所有都需要工作。您正在检查成功/失败,但您需要更加勤奋。例如:除非您的文件以 1K 八位字节的精确倍数结尾,否则最终的部分帧将被忽略(因为 fread 将返回结果 send 调用仅检查非直接失败 (-1),但从未验证是否发送准确请求的八位字节数。像这样的事情很重要
  • 'if (strncmp("!EOF!", data, 5) == 0) {' 不,这不能与 SOCK_STREAM 一起可靠地工作。不能保证这 5 个字节会被一次 recv() 调用加载。
  • 感谢大家的反馈。关于你的评论 G.M. ,这不应该是问题吗?因为 recv() 返回接收到的字节数,并将其存储在“数据”变量中。关于其他建议,我计划以某种方式对其进行更改,即客户端和服务器首先交换文件的大小(以字节为单位)。由于 send() 和 recv() 都返回字节数,我可以继续发送/接收,直到文件大小被发送/接收。然后 send_file 也将是一个 while(1) 循环,并且 fread()/send() 直到达到文件大小。通过按字节打破循环,strncmp() 将不再是必要的。

标签: c sockets file-transfer


【解决方案1】:

我终于在以下帖子的帮助下想通了:

Sending files over TCP sockets C++ | Windows

我重写了示例代码以满足我的需要。到目前为止,它就像一个魅力。

感谢大家让我对这个话题有了更多的了解,并帮助我在路上进行必要的检查!

这里有一个简短的解释新代码:

首先需要认识到的是,像 send() recv() fread() fwrite() 这样的函数在传递之前可能不会完全填满缓冲区。这意味着如果您有一个指定大小为 100 的缓冲区,它们可能只会将其填充到 89 95 或其他大小。因此,文件很可能已损坏。为了解决这个问题,需要检查 send() recv() fread() fwrite() 函数的每次调用。

首先,您需要在服务器端和客户端都使用这两个功能。这些确保正在发送/接收整个缓冲区。 它基本上只是在 send() / recv() 上循环,直到缓冲区被填满。

int RecvBuffer(int sock, char* buffer, int bufferSize, int chunkSize) { 
    int i = 0;
    while (i < bufferSize) {
        const int l = recv(sock, &buffer[i], __min(chunkSize, bufferSize - i), 0);
        if (l < 0) { return l; } 
        i += l;
    }
    return i;
}

int SendBuffer(int sock, char* buffer, int bufferSize, int chunkSize) { 
    int i = 0;
    while (i < bufferSize) {
        const int l = send(sock, &buffer[i], __min(chunkSize, bufferSize - i), 0);
        if (l < 0) { return l; } 
        i += l;
    }
    return i;
}

接下来,我们需要在调用下载/上传文件的函数中应用相同的检查。在这里,我循环遍历填充缓冲区的上述函数和 fread() fwrite(),直到所有字节 (fileSize) 都已发送。 chunkSize 参数定义正在发送的每个包的大小。到目前为止,我使用 65.536 (64kb) 没有任何问题。

int RecvFile(int sock, char *filePath, int chunkSize, int fileSize) { 

    char *lastSlash = strrchr(filePath, '\\');
    char *filename = lastSlash ? lastSlash +1 : filePath;
    FILE *fp = fopen(filename, "wb");

    char buffer[chunkSize];
    int i = fileSize;

    while (i != 0) {
        const int r = RecvBuffer(sock, buffer, (int)__min(i, (int)chunkSize), chunkSize);
        if ((r < 0) || !fwrite(buffer, sizeof(char), r, fp)) { break; }
        i -= r;
    }
    bzero(buffer, sizeof(buffer));

    fclose(fp);

    printf("\n%s%s[+] Download Successful\n\n", KGRN, BGHT);

    return -3;
}

int SendFile(int sock, char *fileName, int chunkSize, int fileSize) { 

    FILE *fp = fopen(fileName, "rb");

    char buffer[chunkSize];
    int i = fileSize;

    while (i != 0) {
        const int ssize = __min(i, (int)chunkSize);
        if (!fread(buffer, sizeof(char), ssize, fp)) { break; }
        const int l = SendBuffer(sock, buffer, (int)ssize, (int)chunkSize);
        if (l < 0) { break; }
        i -= l;
    }
    bzero(buffer, sizeof(buffer));

    fclose(fp);

    printf("\n%s%s[+] Upload Successful\n\n", KGRN, BGHT);

    return -3;
}

【讨论】:

  • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
猜你喜欢
  • 2017-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多