Передача файлов с помощью TransmitFile

Функция TransmitFile служит для быстрой передачи данных из файлов, благодаря тому, что она работает в режиме ядра(kernel mode), и не происходят частые переключения между режимами. Данная функция это дополнение к WinSock, то есть если Вы пишите программу для Win32 и после хотите перенести код на Unix-системы, то придется значительно изменять код. Плюсом функции является то, что программисту не приходится заботиться о правильном чтении и отправки данных, всю работу возлагает на себя WinSock2. Определение функции:


BOOL TransmitFile(
SOCKET hSocket,
HANDLE hFile,
DWORD nNumberOfBytesToWrite,
DWORD nNumberOfBytesPerSend,
LPOVERLAPPED lpOverlapped,
LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
DWORD dwFlags
);

Параметры:

hSocket – подключенный сокет, через который будет иди передача данных, тип сокета должен быть ориентированным на соединение. TransmitFile не поддерживает UDP сокеты;
hFile – описатель открытого файла, данные которого будут передаваться;
nNumberOfBytesToWrite – количество записываемых из файла байт, если 0 то отправляется файл целеком;
nNumberOfBytesPerSend – размер блоков данных для операции записи, если 2048 то передаче будет идти блоками по 2 килобайта, если 0 то размер блоков устанавливается по умолчанию;
lpOverlapped – определяет структуру OVERLAPPED, для перекрытого ввода/вывода;
lpTransmitBuffers – представляет структуру TRANSMIT_FILE_BUFFERS, содержащую информацию, которая передается до и после отправки данных из файла:

typedef struct _TRANSMIT_FILE_BUFFERS {
PVOID Head;
DWORD HeadLength;
PVOID Tail;
DWORD TailLength;
} TRANSMIT_FILE_BUFFERS;

Head – указатель на данные которые передаются до отправки;
HeadLength – количество передаваемой информации;
Tail – указатель на данные которые передаются после отправки;
TailLength — количество передаваемой информации.
dwFlags – флаги для управления работой TransmitFile:

TF_DISCONNECT – закрыть сокет после передачи;
TF_REUSE_SOCKET – использовать сокет для повторной передачи;
TF_USE_DEFAULT_WORKER, TF_USE_SYSTEM_THREAD – передача должна идти в контексте системного процесса, используются при передаче больших файлов;
TF_USE_KERNEL_APC – передача будет идти при помощи асинхронных вызовов процедур(APC), используется если нужна лишь одна процедура чтения;
TF_WRITE_BEHIND – завершить работу без подтверждения о приеме данных.

Для демонстрации работы функции создано тестовое приложение на MFC, которое всю принимаемую информацию будет выводить на экран, исходник приложения находится в архиве к данной статье. Теперь код программы передачи файлов:

#include <Winsock2.h>//ws2_32.lib
#include <Mswsock.h>//Mswsock.lib
#include <windows.h>
#include <iostream.h>
#include <conio.h>
 
int main()
{
        SOCKET sTransmit;
        SOCKADDR_IN InetAddr;
        HANDLE hFile;
        WSADATA wsaData;
        if(!WSAStartup(0x0202,&wsaData))
        {
                if((sTransmit = socket(AF_INET,SOCK_STREAM,0)) != INVALID_SOCKET)
                {
                        InetAddr.sin_family      = AF_INET;
                        InetAddr.sin_addr.s_addr = inet_addr("127.0.0.1");//htonl(INADDR_ANY);
                        InetAddr.sin_port        = htons(4004);
                        if(connect(sTransmit,(struct sockaddr *)&InetAddr,sizeof(InetAddr)) != SOCKET_ERROR)
                        {
                                hFile=CreateFile("---.cpp",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
                                if(hFile != INVALID_HANDLE_VALUE)
                                {
                                        BOOL bResult = TransmitFile(sTransmit,hFile,0/*целеком*/,1024,NULL,0,TF_DISCONNECT);
                                        if(!bResult){cout<<"Error TransmitFile"<<endl;}
                                }else{cout<<"Error CreateFile"<<endl;}
                        }else{cout<<"Error connect"<<endl;}
                }else{cout<<"Error socket"<<endl;}
        }else{cout<<"Error WSAStartup"<<endl;}
        CloseHandle(hFile);
        if(closesocket(sTransmit)==SOCKET_ERROR){cout<<"Error closesocket"<<endl;}
        WSACleanup();
        cout<<"Press any key to continue"<<endl;
        while (!_getch());
        return 0;
}