• TransmitFile函数的简单使用


    简述

    TransmitFile是一个扩展的 API,它允许在套接字连接上发送一个打开的文件。这使得应用程序可以避免亲自打开文件,重复地在文件执行读入操作,再将读入的那块数据写入套接字。相反,已打开的文件的句柄和套接字连接一起给出的,在套接字上,文件数据的读入和发送都在模式下进行。这就避免了多次的用户/内核模式切换。与linux的sendfile函数类似。
    TransmitFile通过已经连接的SOCKET句柄传输文件,使用操作系统的缓冲管理器来接收数据并提供高质量的文件传输。

    https://msdn.microsoft.com/en-us/library/windows/desktop/ms740565(v=vs.85).aspx

    参数

    • hSocket
      一个连接的套接字句柄。函数将文件数据写向这个套接字。其必须是面向连接(TCP)的SOCKET。如果hFile为NULL,lpTransmitBuffers将被传输
    • hFile
      已打开的文件句柄。由系统内核读取文件数据,可以通过FILE_FLAG_SEQUENTIAL_SCAN提高处理缓存性能。
    • nNumberOfBytesToWrite
      要传送的字节数。0值表示传送整个文件。发生错误时,以已发送数据为准。
    • nNumberOfBytesPerSend
      每次传送的数据块的大小。0值表示使用SOCKET LAYER的默认值。
    • lpOverlapped
      指向OVERLAPPED结构的指针。如果hSocket以打开重叠(默认),可指定这个参数,以实现一个重叠IO操作(异步)。NULL值表示不开启overlapped(重叠) I/O模式。
    • lpTransmitBuffers
      指向TRANSMIT_FILE_BUFFERS结构指针。NULL值表示仅仅传输文件。
      dwFlags
      用于修改TransmitFile函数调用行为的标识。可以包含下表中的定义(在mswscok.h文件中)
    标识 含义
    TF_DISCONNECT 在所有文件数据已排队等待传输之后,启动传输层断开连接
    TF_REUSE_SOCKET 准备要重复使用的socket句柄。 此标志仅在指定了TF_DISCONNECT时有效。
    当TransmitFile请求完成时,套接字句柄可以传递到以前用于建立连接的函数调用,例如AcceptEx或ConnectEx。 这种重用是互斥的; 例如,如果为套接字调用了AcceptEx函数,则仅允许重复使用对AcceptEx函数的后续调用,并且不允许对ConnectEx的后续调用。
    注意套接字级文件传输受底层传输的行为的影响。 例如,TCP套接字可能受到TCP TIME_WAIT状态的影响,导致TransmitFile调用被延迟。
    TF_USE_DEFAULT_WORKER 指示要使用系统的默认线程来处理长 TransmitFile 请求的 Windows 套接字服务提供程序。可以使用以下注册表参数作为 REG_DWORD 调整系统默认线程︰
    HKEY_LOCAL_MACHINECurrentControlSetServicesAFDParametersTransmitWorker
    TF_USE_SYSTEM_THREAD 指示Windows Sockets服务提供程序使用系统线程来处理长的TransmitFile请求。
    TF_USE_KERNEL_APC 指示驱动程序使用内核异步过程调用(APC)而不是工作线程来处理长的TransmitFile请求。 Long TransmitFile请求定义为需要从文件或缓存中进行多次读取的请求; 因此请求取决于文件的大小和发送数据包的指定长度。
    使用TF_USE_KERNEL_APC可以提供显着的性能优势。 然而,可能(虽然不太可能),启动上下文TransmitFile的线程正用于大量计算; 这种情况可能会阻止APC发射。 请注意,Winsock内核模式驱动程序使用正常的内核APC,每当线程处于等待状态时启动,这与用户模式APC不同,每当线程处于用户模式启动的警报等待状态时启动。
    TF_WRITE_BEHIND 立即完成TransmitFile请求,无待处理。 如果指定此标志并且TransmitFile成功,则数据已被系统接受,但不一定由远程端确认。 不要在指定TF_DISCONNECT和TF_REUSE_SOCKET标志时使用此设置。

    示例代码

    #include <stdio.h>
    #include <stdlib.h>
    
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #pragma comment(lib,"Ws2_32.lib")
    
    #include <MSWSock.h>
    #pragma comment(lib, "Mswsock.lib ")
    
    int main(int c, char** v)
    {
    	if (c != 4) {
    		printf("用法:%s filename ip port
    ", v[0]);
    		return 0;
    	}
    
    	// 创建并初始化winsock数据变量
    	WSADATA wsaData = { 0 };
    	int iResult = 0;
    
    	SOCKET hSocket = INVALID_SOCKET;
    	int iFamily = AF_INET;
    	int iType = SOCK_STREAM;
    	int iProtocol = IPPROTO_TCP;
    
    	// 初始化 Winsock
    	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    	if (iResult != 0) {
    		printf("WSAStartup failed: %d
    ", iResult);
    		return -1;
    	}
    	// 打开socket
    	hSocket = socket(iFamily, iType, iProtocol);
    	if (hSocket == INVALID_SOCKET) {
    		printf("socket function failed with error = %d
    ", WSAGetLastError());
    		WSACleanup();
    		return -2;
    	}
    	do {
    		// 打开文件
    		HANDLE hFile = CreateFile(v[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
    		if (hFile == INVALID_HANDLE_VALUE){
    			iResult = -3;
    			break;
    		}
    		// 获取文件大小
    		//GetFileSize(hFile, NULL);
    		LARGE_INTEGER liFileSize;
    		if (GetFileSizeEx(hFile, &liFileSize) == FALSE) {
    			iResult = -4;
    			break;
    		}
    		// 设置远程段地址
    		sockaddr_in remoteAddr;
    		remoteAddr.sin_family = AF_INET;
    		//remoteAddr.sin_addr.s_addr = inet_addr(v[2]);
    		inet_pton(AF_INET, v[2], &remoteAddr.sin_addr);
    		remoteAddr.sin_port = htons(atoi(v[3]));
    
    		// 连接到远程端
    		iResult = connect(hSocket, (SOCKADDR *)& remoteAddr, sizeof(remoteAddr));
    		if (iResult == SOCKET_ERROR) {
    			printf("connect function failed with error: %ld
    ", WSAGetLastError());
    			iResult = -5;
    			break;
    		}
    		// 使用TransmitFile发送文件
    		if (TransmitFile(hSocket, hFile, 0, 0, NULL, NULL, TF_USE_DEFAULT_WORKER) == FALSE) {
    			printf("TransmitFile function failed with error: %ld
    ", WSAGetLastError());
    			iResult = -6;
    			break;
    		}
    	} while (0);
    	// 关闭socket
    	iResult = closesocket(hSocket);
    	if (iResult == SOCKET_ERROR) {
    		printf("closesocket failed with error = %d
    ", WSAGetLastError());
    		iResult = -7;
    	}
    	// 清理
    	WSACleanup();
    
    	return iResult;
    }
    
  • 相关阅读:
    Python3笔记029
    Python3笔记028
    Python3笔记027
    Python3笔记026
    Python3笔记025
    Python3笔记024
    find the oracle logs
    asm command
    网上看到的一个朋友写的不错收藏着.
    在IBM AIX上安装Oracle RAC ,很多人都在找的东东.....
  • 原文地址:https://www.cnblogs.com/oloroso/p/5961500.html
Copyright © 2020-2023  润新知