Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transfer a big file though socket

I practice in socket programming, I wrote a code (server: python, client: C/C++) to transfer data though socket. With small file, it works perfectly. Then I try with bigger file, it only transfers part of that file. Here is my code:

Server

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"

Client

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

like image 228
Brian MJ Avatar asked Aug 06 '26 19:08

Brian MJ


2 Answers

Your sending code has many logic errors:

  • lack of adequate error handling.

  • passing the wrong buffer size to send() (hint, it won't be 1024 for the last buffer if the file size is not an even multiple of 1024).

  • assuming send() sends the entire buffer in one go (hint, it rarely does). You need to call send() in a loop until the entire buffer has been sent.

  • misinterpreting the return value of send() (hint, it doesn't return a boolean). It returns the actual number of bytes sent (well, the number of bytes accepted into the socket's internal buffer for transmission in the background).

  • adding the wrong value to total after each send(), since you are assuming send() the entire buffer in one go.

  • sending a delimiter string at the end of the file, without regard to whether such delimiter may have appeared in the file being sent. It would be safer to instead send the file's size before sending the file's bytes.

  • specifying the wrong buffer size when sending the delimiter string (hint, "Done" is not 1024 bytes in size).

  • leaking the filewrite handle. You are not calling fclose() for it.

With that said, try something more like this instead:

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"
like image 174
Remy Lebeau Avatar answered Aug 08 '26 08:08

Remy Lebeau


Done is no more than an arbitrary sequence of 4 bytes. If it is present in the tranfered file, it will stop the transfer. For binary files, the common way is to first send the size and then the file, or to send blocks with a well known structure (for example starting with the block size) and an empty block indicating the end of the file.

like image 43
Serge Ballesta Avatar answered Aug 08 '26 09:08

Serge Ballesta