C/C++ 通过套接字下载/上传文件

C/C++ Downloading / Uploading Files via socket

我目前正在尝试通过套接字下载/上传文件 (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;
}

我终于在以下Post的帮助下弄明白了:

我重写了示例代码以满足我的需要。到目前为止效果很好。

感谢大家让我对这个主题有了更多的了解,并帮助我确定了我们在途中进行的必要检查!

这里对新代码进行简要说明:

首先需要认识到的是,像 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;
}