重叠I/O的基本原理是让应用程序使用重叠的数据结构,投递一个或多个I/O请求。
在winsock中的重叠数据结构定义是WSAOVERLAPPED,用来完成重叠I/O的方式有两种,1事件通知 2完成实例
在事件通知方式中,通过WSAOVERLAPPED结构将每个socket和事件关联在一起,通过调用WSARcev等非阻塞函数,将WSAOVERLAPPED结构体与其关联。调用WSAWaitForMultipleEvents函数等待事件,这个函数可以放到另外一个线程中进行处理,当数据到达后会存放到之前WSARcev中的buf中。
编程顺序如下:
1 创建一个套接字,开始在指定的端口上侦听
2 接收一个入站的连接请求
3 为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄。也将该事件对象分配给一个事件数组,以便稍后由WSAWaitForMultipleEvents函数使用。
4 将WSAOVERLAPPED结构指定参数,在套接字上投递一个异步WSARecv请求。
5 使用步骤3的数组,调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件。
6 使用WSAGetOverlappedResult函数,判断重叠调用的返回状态
7 函数完成后,针对事件数组,调用WSAResetEvent函数,重设事件对象,并对完成的事件请求进行处理。
8 在套接字上重新投递另一个WSARecv请求
9 重复步骤5-8
#include <winsock2.h> #include <stdio.h> #define PORT 5500 #define MSGSIZE 1024 #pragma comment(lib, "ws2_32.lib") typedef struct { WSAOVERLAPPED overlap; WSABUF Buffer; char szMessage[MSGSIZE]; DWORD NumberOfBytesRecvd; DWORD Flags; }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA; int g_iTotalConn = 0; SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS]; WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS]; LPPER_IO_OPERATION_DATA g_pPerIODataArr[MAXIMUM_WAIT_OBJECTS]; DWORD WINAPI WorkerThread(LPVOID); void Cleanup(int); int main() { WSADATA wsaData; SOCKET sListen, sClient; SOCKADDR_IN local, client; DWORD dwThreadId; int iaddrSize = sizeof(SOCKADDR_IN); // Initialize Windows Socket library WSAStartup(0x0202, &wsaData); // Create listening socket sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Bind local.sin_addr.S_un.S_addr = htonl(INADDR_ANY); local.sin_family = AF_INET; local.sin_port = htons(PORT); bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN)); // Listen listen(sListen, 3); // Create worker thread CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId); while (TRUE) { // Accept a connection sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize); printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); g_CliSocketArr[g_iTotalConn] = sClient; // Allocate a PER_IO_OPERATION_DATA structure g_pPerIODataArr[g_iTotalConn] = (LPPER_IO_OPERATION_DATA)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_OPERATION_DATA)); g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE; g_pPerIODataArr[g_iTotalConn]->Buffer.buf = g_pPerIODataArr[g_iTotalConn]->szMessage; g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent(); // Launch an asynchronous operation WSARecv( g_CliSocketArr[g_iTotalConn], &g_pPerIODataArr[g_iTotalConn]->Buffer, 1, &g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd, &g_pPerIODataArr[g_iTotalConn]->Flags, &g_pPerIODataArr[g_iTotalConn]->overlap, NULL); g_iTotalConn++; } closesocket(sListen); WSACleanup(); return 0; } DWORD WINAPI WorkerThread(LPVOID lpParam) { int ret, index; DWORD cbTransferred; while (TRUE) { ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE); if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT) { continue; } index = ret - WSA_WAIT_EVENT_0; WSAResetEvent(g_CliEventArr[index]); WSAGetOverlappedResult( g_CliSocketArr[index], &g_pPerIODataArr[index]->overlap, &cbTransferred, TRUE, &g_pPerIODataArr[g_iTotalConn]->Flags); if (cbTransferred == 0) { // The connection was closed by client Cleanup(index); } else { // g_pPerIODataArr[index]->szMessage contains the received data g_pPerIODataArr[index]->szMessage[cbTransferred] = '\0'; send(g_CliSocketArr[index], g_pPerIODataArr[index]->szMessage,cbTransferred, 0); // Launch another asynchronous operation WSARecv( g_CliSocketArr[index], &g_pPerIODataArr[index]->Buffer, 1, &g_pPerIODataArr[index]->NumberOfBytesRecvd, &g_pPerIODataArr[index]->Flags, &g_pPerIODataArr[index]->overlap, NULL); } } return 0; } void Cleanup(int index) { closesocket(g_CliSocketArr[index]); WSACloseEvent(g_CliEventArr[index]); HeapFree(GetProcessHeap(), 0, g_pPerIODataArr[index]); if (index < g_iTotalConn - 1) { g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1]; g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1]; g_pPerIODataArr[index] = g_pPerIODataArr[g_iTotalConn - 1]; } g_pPerIODataArr[--g_iTotalConn] = NULL; }