Передача большого файла через сокет - PullRequest
3 голосов
/ 30 октября 2019

Я практикую в программировании сокетов, я написал код (сервер: 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

Ответы [ 2 ]

4 голосов
/ 30 октября 2019

Done - не более чем произвольная последовательность из 4 байтов. Если он присутствует в переданном файле, он остановит передачу. Для двоичных файлов обычным способом является сначала отправить размер, а затем файл или отправить блоки с хорошо известной структурой (например, начиная с размера блока) и пустым блоком, указывающим конец файла.

3 голосов
/ 30 октября 2019

Код отправки имеет много логических ошибок:

  • отсутствие адекватной обработки ошибок.

  • передача неверного размера буфера в send()(подсказка, для последнего буфера не будет 1024, если размер файла не кратен 1024).

  • при условии, что send() отправляет весь буфер за один раз (намек, это редко бывает). Вам нужно вызывать send() в цикле, пока не будет отправлен весь буфер.

  • неверно истолковывает возвращаемое значение send() (подсказка, оно не возвращает логическое значение). Возвращает фактическое количество отправленных байтов (ну, число байтов, принятых во внутренний буфер сокета для передачи в фоновом режиме).

  • добавляя неправильное значение к total после каждогоsend(), поскольку предполагается, что 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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...