通过套接字传输大文件

3

我在套接字编程方面有经验,我编写了一段代码(服务器端:Python,客户端:C / C ++),通过套接字传输数据。它可以完美传输小文件。但是当我尝试传输大文件时,它只传输了该文件的一部分。以下是我的代码:

服务器端

def recv_file_from_client(conn):
    f = open("torecv","wb")
    while(True):
        l = conn.recv(1024)
        if "Done" in l:
            break
        f.write(l)
    f.close()
    print "Done recv"

客户端

BOOL SendFile(TCHAR* file) {
    FILE* filewrite = fopen("test.txt", "a");
    FILE* fp = _wfopen(file, L"rb");
    unsigned char buffer[1024] = { NULL };
    int readedChar;
    int total = 0;
    char log[128] = { NULL };
    while ((readedChar = fread(buffer, 1, 1024, fp)) > 0) {
        if (send(s, (const char*)buffer, sizeof(buffer), 0)) {
            total += readedChar;
            sprintf(log, "%d\n", total);
            fputs(log,filewrite);
        }
        memset(buffer, 0, 1024);
    }
    send(s, "Done", 1024, 0);
    fclose(fp);
    return TRUE;
}

Result


1
客户端:C/C++。并不存在所谓的 C/C++ 语言。C 和 C++ 是完全不同的语言。因此,请选择其中一种,尽可能避免滥用语言标签。 - Fareanor
1
文件中是否有“Done”字符串的ASCII值? - mch
1
你必须检查send()的返回代码。一个大于0的值是传递到发送缓冲区的字节数,这可能少于您要发送的字节数。(在非阻塞模式下,甚至可以为0。)您需要增加缓冲指针并相应地减少要发送的字节数,并重试直到达到缓冲区的末尾。如果出现错误,则返回小于0(-1)的值。与fread类似:您有readedChar中的项目(字节)数量,这可能少于1024,但您总是尝试发送sizeof(buffer),因此您可能会发送未定义/旧数据从buffer中。 - Bodo
@Fareanor 对不起,我还是新手。 - Brian MJ
显示剩余4条评论
2个回答

4

Done只是一个任意的4字节序列。如果它出现在传输的文件中,将停止传输。对于二进制文件,通常的方式是先发送大小,然后发送文件,或者发送具有已知结构(例如以块大小开头)和表示文件结尾的空块。


4

你的发送代码存在许多逻辑错误:

  • 缺乏足够的错误处理。

  • send()传递错误的缓冲区大小(提示:如果文件大小不是1024的整数倍,则最后一个缓冲区的大小不会为1024)。

  • 假设send()一次性发送整个缓冲区(提示:它很少这样做)。您需要循环调用send(),直到整个缓冲区都被发送。

  • 误解send()的返回值(提示:它不返回布尔值)。它返回实际发送的字节数(也就是被接受到套接字内部缓冲区中以便在后台传输的字节数量)。

  • 在每次send()之后将错误的值添加到total中,因为您假设send()一次性发送整个缓冲区。

  • 在文件末尾发送分隔符字符串,而无论该分隔符是否出现在发送的文件中。更安全的做法是在发送文件字节之前发送文件大小。

  • 在发送分隔符字符串时指定了错误的缓冲区大小(提示:"Done"不是1024字节大小)。

  • 未关闭filewrite句柄。您没有调用fclose()关闭它。

因此,请尝试使用类似以下的代码:

BOOL SendRaw(const void *buffer, int size) {
    const char *ptr = (const char*)buffer;
    int numSent;
    while (size > 0) {
        numSent = send(s, ptr, size, 0);
        if (numSent == -1)
            return FALSE;
        ptr += numSent;
        size -= numSent;
    }
    return TRUE;
}

BOOL SendFile(const wchar_t* file) {
    FILE* filewrite = fopen("test.txt", "a");
    if (!filewrite)
        return FALSE;

    FILE* fp = _wfopen(file, L"rb");
    if (!fp) {
        fclose(filewrite);
        return FALSE;
    }

    if (fseek(fp, 0, SEEK_END) != 0) {
        fclose(fp);
        fclose(filewrite);
        return FALSE;
    }

    long size = ftell(fp);
    if (size == -1L) {
        fclose(fp);
        fclose(filewrite);
        return FALSE;
    }

    rewind(fp);

    uint32_t tmp = htonl(size);
    if (!SendRaw(&tmp, sizeof(tmp))) {
        fclose(fp);
        fclose(filewrite);
        return FALSE;
    }

    unsigned char buffer[1024];
    int numBytes, numSent, total = 0;

    while (size > 0) {
        numBytes = fread(buffer, 1, min(sizeof(buffer), size), fp);
        if (numBytes < 1) {
            fclose(fp);
            fclose(filewrite);
            return FALSE;
        }
        if (!SendRaw(buffer, numBytes)) {
            fclose(fp);
            fclose(filewrite);
            return FALSE;
        }
        size -= numBytes;
        total += numBytes;
        fprintf(filewrite, "%d\n", total);
    }

    fclose(fp);
    fclose(filewrite);
    return TRUE;
}

import struct

def recv_file_from_client(conn):
    f = open("torecv","wb")
    data = conn.recv(4)
    if not data:
        print "Error recv"
        return
    size = struct.unpack("!I", data)[0]
    while(size > 0):
        data = conn.recv(min(1024, size))
        if not data:
            print "Error recv"
            return
        f.write(data)
        size -= len(data)
    f.close()
    print "Done recv"

1
非常感谢您的评论,它帮助我很多地理解了在发送和接收时应该做些什么。 - Brian MJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接