Как передать файлы размером более 2 147 483 646 байт (~2 ГиБ) с помощью Win32 TransmitFile()?

Цитируется из записи MSDN для TransmitFile:

Максимальное количество байтов, которое может быть передано с помощью одного вызова функции TransmitFile, составляет 2 147 483 646, максимальное значение для 32-разрядного целого числа минус 1. Максимальное количество байтов, отправляемых за один вызов, включает любые данные, отправленные до или после данные файла, на которые указывает параметр lpTransmitBuffers, плюс значение, указанное в параметре nNumberOfBytesToWrite для длины данных файла для отправки. Если приложению необходимо передать файл, размер которого превышает 2 147 483 646 байт, то можно использовать несколько вызовов функции TransmitFile, при этом каждый вызов передает не более 2 147 483 646 байт. Установка параметра nNumberOfBytesToWrite в ноль для файла размером более 2 147 483 646 байт также завершится ошибкой, поскольку в этом случае функция TransmitFile будет использовать размер файла в качестве значения для количества байтов для передачи.

Хорошо. Отправка файла размером 2*2,147,483,646 bytes (~ 4 ГиБ) с TransmitFile затем должен быть разделен как минимум на две части (например, 2 ГиБ + 2 ГиБ при двух вызовах TransmitFile). Но как именно это можно сделать, предпочтительно также поддерживая основное TCP-соединение между ними?

Когда размер файла действительно <=2 147 483 646 байт, можно просто написать:

HANDLE fh = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, 
 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);   

TransmitFile(SOCK_STREAM_socket, fh, 0, 0, NULL, NULL, TF_DISCONNECT);

позволить Windows обрабатывать все вещи более низкого уровня (кэширование, разбиение данных на части для эффективной передачи и т. д. Однако, в отличие от сопоставимого системного вызова sendfile() в Linux, в вызове нет очевидного аргумента смещения (хотя пятый аргумент), LPOVERLAPPED lpOverlapped наверное это именно то, что я ищу). Я полагаю, что я мог бы взломать что-то вместе, но я также ищу изящное, хорошее решение Win32 от кого-то, кто действительно знает об этом.

Вы можете использовать параметр lpOverlapped, чтобы указать 64-битное смещение в файле, с которого начинается передача данных файла, установив элемент Offset и OffsetHigh структуры OVERLAPPED. Если lpOverlapped является указателем NULL, передача данных всегда начинается с текущего байтового смещения в файле.

Итак, из-за отсутствия минимального примера, легко доступного в сети, какие вызовы необходимы для выполнения такой задачи?

1 ответ

Решение

Удалось выяснить это по комментариям.

Так что если LPOVERLAPPED lpOverlapped нулевой указатель, вызов начинает передачу с текущего смещения файла (очень похоже на Linux sendfile() системный вызов и его off_t *offset параметр). Этим смещением (указателем) можно манипулировать с помощью SetFilePointerExтак можно написать:

#define TRANSMITFILE_MAX ((2<<30) - 1)

LARGE_INTEGER total_bytes;
memset(&total_bytes, 0, sizeof(total_bytes));

while (total_bytes < filesize) {
     DWORD bytes = MIN(filesize-total_bytes, TRANSMITFILE_MAX);
     if (!TransmitFile(SOCK_STREAM_socket, fh, bytes, 0, NULL, NULL, 0))
     { /* error handling */ }

     total_bytes.HighPart += bytes;
     SetFilePointerEx(fh, total_bytes, NULL, FILE_BEGIN);
}

closesocket(SOCK_STREAM_socket);

выполнить задачу. Не очень элегантно, но это работает.

Другие вопросы по тегам