Winsock IO模型之Overlapped模型

这个模型的基本思想是使用重叠数据结构一次投递一个或多个异步I/O请求。当提交的I/O请求完成之后,与之关联的重叠数据结构中的事件对象受信,应用程序便可使用WSAGetOverlappedResult函数获取重叠操作结果。这个和使用重叠结构调用ReadFile和WriteFile函数操作文件类似。

    使用这个模型,必须要搞清楚以下这几个异步I/O函数:WSASocket,AcceptEx,WSASend(UDP的:WSASendTo),WSARecv(UDP的:WSARecvFrom),WSAGetOverlappedResult,最关键的还需要知道WSAOVERLAPPED在这些异步IO调用中所起的作用(其实是通过事件),以及WSABUF(WSASend/WSARecv类似函数中需要填入的一个重要参数,在投递完后未受信之前这块buffer将被system锁定)结构体和OVERLAPPED(比WSAOVERLAPPED看起来更通用,但其本质是一样的,WSAOVERLAPPED名称看起来多了WSA)结构体,最后需要与那些通用的I/O函数(如recv,send等)进行对比才能理解得更深。

    以下是从《Windows网络与通信程序设计》中摘出来的几条注意点:

    以下是这本书上给出的一个tcp回显的实例:

[cpp]  view plain copy
  1. ///  
  2. // OverlappedServer.cpp文件  
  3.   
  4. #include "../common/initsock.h"  
  5.   
  6. #include <Mswsock.h>  
  7. #include <stdio.h>  
  8. #include <windows.h>  
  9.   
  10. CInitSock theSock;  
  11.   
  12. #define BUFFER_SIZE 1024  
  13.   
  14. typedef struct _SOCKET_OBJ  
  15. {  
  16.     SOCKET s;                       // 套节字句柄  
  17.     int nOutstandingOps;            // 记录此套节字上的重叠I/O数量  
  18.       
  19.     LPFN_ACCEPTEX lpfnAcceptEx;     // 扩展函数AcceptEx的指针(仅对监听套节字而言)  
  20. } SOCKET_OBJ, *PSOCKET_OBJ;  
  21.   
  22. typedef struct _BUFFER_OBJ  
  23. {     
  24.     OVERLAPPED ol;          // 重叠结构  
  25.     char *buff;             // send/recv/AcceptEx所使用的缓冲区  
  26.     int nLen;               // buff的长度  
  27.     PSOCKET_OBJ pSocket;    // 此I/O所属的套节字对象  
  28.   
  29.     int nOperation;         // 提交的操作类型  
  30. #define OP_ACCEPT   1  
  31. #define OP_READ     2  
  32. #define OP_WRITE    3  
  33.   
  34.     SOCKET sAccept;         // 用来保存AcceptEx接受的客户套节字(仅对监听套节字而言)  
  35.     _BUFFER_OBJ *pNext;  
  36. } BUFFER_OBJ, *PBUFFER_OBJ;  
  37.   
  38. HANDLE g_events[WSA_MAXIMUM_WAIT_EVENTS];   // I/O事件句柄数组  
  39. int g_nBufferCount;                         // 上数组中有效句柄数量  
  40. PBUFFER_OBJ g_pBufferHead, g_pBufferTail;   // 记录缓冲区对象组成的表的地址  
  41.   
  42. // 申请套节字对象和释放套节字对象的函数  
  43. PSOCKET_OBJ GetSocketObj(SOCKET s)  
  44. {  
  45.     PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));  
  46.     if(pSocket != NULL)  
  47.     {  
  48.         pSocket->s = s;  
  49.     }  
  50.     return pSocket;  
  51. }  
  52. void FreeSocketObj(PSOCKET_OBJ pSocket)  
  53. {  
  54.     if(pSocket->s != INVALID_SOCKET)  
  55.         ::closesocket(pSocket->s);  
  56.     ::GlobalFree(pSocket);  
  57. }  
  58.   
  59. PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket, ULONG nLen)  
  60. {  
  61.     if(g_nBufferCount > WSA_MAXIMUM_WAIT_EVENTS - 1)  
  62.         return NULL;  
  63.   
  64.     PBUFFER_OBJ pBuffer = (PBUFFER_OBJ)::GlobalAlloc(GPTR, sizeof(BUFFER_OBJ));  
  65.     if(pBuffer != NULL)  
  66.     {  
  67.         pBuffer->buff = (char*)::GlobalAlloc(GPTR, nLen);  
  68.         pBuffer->ol.hEvent = ::WSACreateEvent();  
  69.         pBuffer->pSocket = pSocket;  
  70.         pBuffer->sAccept = INVALID_SOCKET;  
  71.   
  72.         // 将新的BUFFER_OBJ添加到列表中  
  73.         if(g_pBufferHead == NULL)  
  74.         {  
  75.             g_pBufferHead = g_pBufferTail = pBuffer;  
  76.         }  
  77.         else  
  78.         {  
  79.             g_pBufferTail->pNext = pBuffer;  
  80.             g_pBufferTail = pBuffer;  
  81.         }  
  82.         g_events[++ g_nBufferCount] = pBuffer->ol.hEvent;  
  83.     }  
  84.     return pBuffer;  
  85. }  
  86.   
  87. void FreeBufferObj(PBUFFER_OBJ pBuffer)  
  88. {  
  89.     // 从列表中移除BUFFER_OBJ对象  
  90.     PBUFFER_OBJ pTest = g_pBufferHead;  
  91.     BOOL bFind = FALSE;  
  92.     if(pTest == pBuffer)  
  93.     {  
  94.         g_pBufferHead = g_pBufferTail = NULL;  
  95.         bFind = TRUE;  
  96.     }  
  97.     else  
  98.     {  
  99.         while(pTest != NULL && pTest->pNext != pBuffer)  
  100.             pTest = pTest->pNext;  
  101.         if(pTest != NULL)  
  102.         {  
  103.             pTest->pNext = pBuffer->pNext;  
  104.             if(pTest->pNext == NULL)  
  105.                 g_pBufferTail = pTest;  
  106.             bFind = TRUE;  
  107.         }  
  108.     }  
  109.     // 释放它占用的内存空间  
  110.     if(bFind)  
  111.     {  
  112.         g_nBufferCount --;  
  113.         ::CloseHandle(pBuffer->ol.hEvent);  
  114.         ::GlobalFree(pBuffer->buff);  
  115.         ::GlobalFree(pBuffer);    
  116.     }  
  117. }  
  118.   
  119. PBUFFER_OBJ FindBufferObj(HANDLE hEvent)  
  120. {  
  121.     PBUFFER_OBJ pBuffer = g_pBufferHead;  
  122.     while(pBuffer != NULL)  
  123.     {  
  124.         if(pBuffer->ol.hEvent == hEvent)  
  125.             break;  
  126.         pBuffer = pBuffer->pNext;  
  127.     }  
  128.     return pBuffer;  
  129. }  
  130.   
  131. void RebuildArray()  
  132. {  
  133.     PBUFFER_OBJ pBuffer = g_pBufferHead;  
  134.     int i =  1;  
  135.     while(pBuffer != NULL)  
  136.     {  
  137.         g_events[i++] = pBuffer->ol.hEvent;  
  138.         pBuffer = pBuffer->pNext;  
  139.     }  
  140. }  
  141.   
  142. BOOL PostAccept(PBUFFER_OBJ pBuffer)  
  143. {  
  144.     PSOCKET_OBJ pSocket = pBuffer->pSocket;  
  145.     if(pSocket->lpfnAcceptEx != NULL)  
  146.     {     
  147.         // 设置I/O类型,增加套节字上的重叠I/O计数  
  148.         pBuffer->nOperation = OP_ACCEPT;  
  149.         pSocket->nOutstandingOps ++;  
  150.   
  151.         // 投递此重叠I/O    
  152.         DWORD dwBytes;  
  153.         pBuffer->sAccept =   
  154.             ::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);  
  155.         BOOL b = pSocket->lpfnAcceptEx(pSocket->s,   
  156.             pBuffer->sAccept,  
  157.             pBuffer->buff,   
  158.             BUFFER_SIZE - ((sizeof(sockaddr_in) + 16) * 2),  
  159.             sizeof(sockaddr_in) + 16,   
  160.             sizeof(sockaddr_in) + 16,   
  161.             &dwBytes,   
  162.             &pBuffer->ol);  
  163.         if(!b)  
  164.         {  
  165.             if(::WSAGetLastError() != WSA_IO_PENDING)  
  166.                 return FALSE;  
  167.         }  
  168.         return TRUE;  
  169.     }  
  170.     return FALSE;  
  171. };  
  172.   
  173. BOOL PostRecv(PBUFFER_OBJ pBuffer)  
  174. {     
  175.     // 设置I/O类型,增加套节字上的重叠I/O计数  
  176.     pBuffer->nOperation = OP_READ;  
  177.     pBuffer->pSocket->nOutstandingOps ++;  
  178.   
  179.     // 投递此重叠I/O  
  180.     DWORD dwBytes;  
  181.     DWORD dwFlags = 0;  
  182.     WSABUF buf;  
  183.     buf.buf = pBuffer->buff;  
  184.     buf.len = pBuffer->nLen;  
  185.     if(::WSARecv(pBuffer->pSocket->s, &buf, 1, &dwBytes, &dwFlags, &pBuffer->ol, NULL) != NO_ERROR)  
  186.     {  
  187.         if(::WSAGetLastError() != WSA_IO_PENDING)  
  188.             return FALSE;  
  189.     }  
  190.     return TRUE;  
  191. }  
  192.   
  193. BOOL PostSend(PBUFFER_OBJ pBuffer)  
  194. {  
  195.     // 设置I/O类型,增加套节字上的重叠I/O计数  
  196.     pBuffer->nOperation = OP_WRITE;  
  197.     pBuffer->pSocket->nOutstandingOps ++;  
  198.   
  199.     // 投递此重叠I/O  
  200.     DWORD dwBytes;  
  201.     DWORD dwFlags = 0;  
  202.     WSABUF buf;  
  203.     buf.buf = pBuffer->buff;  
  204.     buf.len = pBuffer->nLen;  
  205.     if(::WSASend(pBuffer->pSocket->s,   
  206.             &buf, 1, &dwBytes, dwFlags, &pBuffer->ol, NULL) != NO_ERROR)  
  207.     {  
  208.         if(::WSAGetLastError() != WSA_IO_PENDING)  
  209.             return FALSE;  
  210.     }  
  211.     return TRUE;  
  212. }  
  213.   
  214. BOOL HandleIO(PBUFFER_OBJ pBuffer)  
  215. {  
  216.     PSOCKET_OBJ pSocket = pBuffer->pSocket; // 从BUFFER_OBJ对象中提取SOCKET_OBJ对象指针,为的是方便引用  
  217.     pSocket->nOutstandingOps --;  
  218.   
  219.     // 获取重叠操作结果  
  220.     DWORD dwTrans;  
  221.     DWORD dwFlags;  
  222.     BOOL bRet = ::WSAGetOverlappedResult(pSocket->s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags);  
  223.     if(!bRet)  
  224.     {  
  225.         // 在此套节字上有错误发生,因此,关闭套节字,移除此缓冲区对象。  
  226.         // 如果没有其它抛出的I/O请求了,释放此缓冲区对象,否则,等待此套节字上的其它I/O也完成  
  227.         if(pSocket->s != INVALID_SOCKET)  
  228.         {  
  229.             ::closesocket(pSocket->s);  
  230.             pSocket->s = INVALID_SOCKET;  
  231.         }  
  232.   
  233.         if(pSocket->nOutstandingOps == 0)  
  234.             FreeSocketObj(pSocket);   
  235.           
  236.         FreeBufferObj(pBuffer);  
  237.         return FALSE;  
  238.     }  
  239.   
  240.     // 没有错误发生,处理已完成的I/O  
  241.     switch(pBuffer->nOperation)  
  242.     {  
  243.     case OP_ACCEPT: // 接收到一个新的连接,并接收到了对方发来的第一个封包  
  244.         {  
  245.             // 为新客户创建一个SOCKET_OBJ对象  
  246.             PSOCKET_OBJ pClient = GetSocketObj(pBuffer->sAccept);  
  247.   
  248.             // 为发送数据创建一个BUFFER_OBJ对象,这个对象会在套节字出错或者关闭时释放  
  249.             PBUFFER_OBJ pSend = GetBufferObj(pClient, BUFFER_SIZE);   
  250.             if(pSend == NULL)  
  251.             {  
  252.                 printf(" Too much connections! \n");  
  253.                 FreeSocketObj(pClient);  
  254.                 return FALSE;  
  255.             }  
  256.             RebuildArray();  
  257.               
  258.             // 将数据复制到发送缓冲区  
  259.             pSend->nLen = dwTrans;  
  260.             memcpy(pSend->buff, pBuffer->buff, dwTrans);  
  261.   
  262.             // 投递此发送I/O(将数据回显给客户)  
  263.             if(!PostSend(pSend))  
  264.             {  
  265.                 // 万一出错的话,释放上面刚申请的两个对象  
  266.                 FreeSocketObj(pSocket);   
  267.                 FreeBufferObj(pSend);  
  268.                 return FALSE;  
  269.             }  
  270.             // 继续投递接受I/O  
  271.             PostAccept(pBuffer);  
  272.         }  
  273.         break;  
  274.     case OP_READ:   // 接收数据完成  
  275.         {  
  276.             if(dwTrans > 0)  
  277.             {  
  278.                 // 创建一个缓冲区,以发送数据。这里就使用原来的缓冲区  
  279.                 PBUFFER_OBJ pSend = pBuffer;  
  280.                 pSend->nLen = dwTrans;  
  281.                   
  282.                 // 投递发送I/O(将数据回显给客户)  
  283.                 PostSend(pSend);  
  284.             }  
  285.             else    // 套节字关闭  
  286.             {  
  287.       
  288.                 // 必须先关闭套节字,以便在此套节字上投递的其它I/O也返回  
  289.                 if(pSocket->s != INVALID_SOCKET)  
  290.                 {  
  291.                     ::closesocket(pSocket->s);  
  292.                     pSocket->s = INVALID_SOCKET;  
  293.                 }  
  294.   
  295.                 if(pSocket->nOutstandingOps == 0)  
  296.                     FreeSocketObj(pSocket);       
  297.                   
  298.                 FreeBufferObj(pBuffer);  
  299.                 return FALSE;  
  300.             }  
  301.         }  
  302.         break;  
  303.     case OP_WRITE:      // 发送数据完成  
  304.         {  
  305.             if(dwTrans > 0)  
  306.             {  
  307.                 // 继续使用这个缓冲区投递接收数据的请求  
  308.                 pBuffer->nLen = BUFFER_SIZE;  
  309.                 PostRecv(pBuffer);  
  310.             }  
  311.             else    // 套节字关闭  
  312.             {  
  313.                 // 同样,要先关闭套节字  
  314.                 if(pSocket->s != INVALID_SOCKET)  
  315.                 {  
  316.                     ::closesocket(pSocket->s);  
  317.                     pSocket->s = INVALID_SOCKET;  
  318.                 }  
  319.   
  320.                 if(pSocket->nOutstandingOps == 0)  
  321.                     FreeSocketObj(pSocket);   
  322.   
  323.                 FreeBufferObj(pBuffer);  
  324.                 return FALSE;  
  325.             }  
  326.         }  
  327.         break;  
  328.     }  
  329.     return TRUE;  
  330. }  
  331.   
  332.   
  333. void main()  
  334. {  
  335.     // 创建监听套节字,绑定到本地端口,进入监听模式  
  336.     int nPort = 4567;  
  337.     SOCKET sListen =   
  338.         ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);  
  339.     SOCKADDR_IN si;  
  340.     si.sin_family = AF_INET;  
  341.     si.sin_port = ::ntohs(nPort);  
  342.     si.sin_addr.S_un.S_addr = INADDR_ANY;  
  343.     ::bind(sListen, (sockaddr*)&si, sizeof(si));  
  344.     ::listen(sListen, 200);  
  345.   
  346.     // 为监听套节字创建一个SOCKET_OBJ对象  
  347.     PSOCKET_OBJ pListen = GetSocketObj(sListen);  
  348.   
  349.     // 加载扩展函数AcceptEx  
  350.     GUID GuidAcceptEx = WSAID_ACCEPTEX;  
  351.     DWORD dwBytes;  
  352.     WSAIoctl(pListen->s,   
  353.         SIO_GET_EXTENSION_FUNCTION_POINTER,   
  354.         &GuidAcceptEx,   
  355.         sizeof(GuidAcceptEx),  
  356.         &pListen->lpfnAcceptEx,   
  357.         sizeof(pListen->lpfnAcceptEx),   
  358.         &dwBytes,   
  359.         NULL,   
  360.         NULL);  
  361.   
  362.     // 创建用来重新建立g_events数组的事件对象  
  363.     g_events[0] = ::WSACreateEvent();  
  364.   
  365.     // 在此可以投递多个接受I/O请求  
  366.     for(int i=0; i<5; i++)  
  367.     {  
  368.         PostAccept(GetBufferObj(pListen, BUFFER_SIZE));  
  369.     }  
  370.     ::WSASetEvent(g_events[0]);  
  371.       
  372.     while(TRUE)  
  373.     {  
  374.         int nIndex =   
  375.             ::WSAWaitForMultipleEvents(g_nBufferCount + 1, g_events, FALSE, WSA_INFINITE, FALSE);  
  376.         if(nIndex == WSA_WAIT_FAILED)  
  377.         {  
  378.             printf("WSAWaitForMultipleEvents() failed \n");  
  379.             break;  
  380.         }  
  381.         nIndex = nIndex - WSA_WAIT_EVENT_0;  
  382.         for(int i=0; i<=nIndex; i++) // 这里我认为应该改为:for(int i = nIndex; i <= g_nBufferCount; i++)  
  383.         {  
  384.             int nRet = ::WSAWaitForMultipleEvents(1, &g_events[i], TRUE, 0, FALSE);  
  385.             if(nRet == WSA_WAIT_TIMEOUT)  
  386.                 continue;  
  387.             else  
  388.             {  
  389.                 ::WSAResetEvent(g_events[i]);  
  390.                 // 重新建立g_events数组  
  391.                 if(i == 0)  
  392.                 {  
  393.                     RebuildArray();  
  394.                     continue;  
  395.                 }  
  396.   
  397.                 // 处理这个I/O  
  398.                 PBUFFER_OBJ pBuffer = FindBufferObj(g_events[i]);  
  399.                 if(pBuffer != NULL)  
  400.                 {  
  401.                     if(!HandleIO(pBuffer))  
  402.                         RebuildArray();  
  403.                 }  
  404.             }  
  405.         }  
  406.     }  
  407. }  


参考:王艳平 张越 《Windows网络与通信程序设计》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 下面是一个简单的基于IOCP模型的服务端C代码示例: ``` #include <stdio.h> #include <stdlib.h> #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32.lib") #define MAX_CLIENTS 64 #define BUFFER_SIZE 4096 // 客户端数据结构 typedef struct _CLIENT { SOCKET socket; SOCKADDR_IN address; OVERLAPPED overlapped; WSABUF wsa_buffer; char buffer[BUFFER_SIZE]; } CLIENT, *PCLIENT; // IO操作类型 typedef enum _IO_OPERATION_TYPE { IO_READ, IO_WRITE } IO_OPERATION_TYPE, *PIO_OPERATION_TYPE; // IO操作数据结构 typedef struct _IO_OPERATION_DATA { IO_OPERATION_TYPE type; PCLIENT client; DWORD bytes_transferred; } IO_OPERATION_DATA, *PIO_OPERATION_DATA; HANDLE completion_port; // 完成端口句柄 PCLIENT clients[MAX_CLIENTS]; // 客户端数组 int client_count = 0; // 客户端计数器 // 添加客户端到客户端数组 void add_client(PCLIENT client) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if (clients[i] == NULL) { clients[i] = client; client_count++; break; } } } // 从客户端数组中移除客户端 void remove_client(PCLIENT client) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if (clients[i] == client) { clients[i] = NULL; client_count--; break; } } } // 异步读取客户端数据 void start_io_read(PCLIENT client) { DWORD flags = 0; IO_OPERATION_DATA* io_data = (IO_OPERATION_DATA*)malloc(sizeof(IO_OPERATION_DATA)); memset(&io_data->overlapped, 0, sizeof(OVERLAPPED)); io_data->type = IO_READ; io_data->client = client; io_data->bytes_transferred = 0; client->wsa_buffer.buf = client->buffer; client->wsa_buffer.len = BUFFER_SIZE; WSARecv(client->socket, &client->wsa_buffer, 1, &io_data->bytes_transferred, &flags, &io_data->overlapped, NULL); } // 异步写入数据到客户端 void start_io_write(PCLIENT client, char* buffer, int length) { DWORD flags = 0; IO_OPERATION_DATA* io_data = (IO_OPERATION_DATA*)malloc(sizeof(IO_OPERATION_DATA)); memset(&io_data->overlapped, 0, sizeof(OVERLAPPED)); io_data->type = IO_WRITE; io_data->client = client; io_data->bytes_transferred = 0; client->wsa_buffer.buf = buffer; client->wsa_buffer.len = length; WSASend(client->socket, &client->wsa_buffer, 1, &io_data->bytes_transferred, flags, &io_data->overlapped, NULL); } // 完成端口线程函数 DWORD WINAPI completion_port_thread(LPVOID lpParam) { PIO_OPERATION_DATA io_data; PCLIENT client; DWORD bytes_transferred; DWORD flags; while (TRUE) { if (GetQueuedCompletionStatus(completion_port, ### 回答2: IOCP(Input Output Completion Ports)模型是一种高效的异步IO模型,常用于高性能的服务器端开发。 下面是一个基于IOCP模型的服务端C代码的示例: ```c #include <stdio.h> #include <stdlib.h> #include <winsock2.h> #include <windows.h> #define MAX_CLIENTS 100 #define BUFFER_SIZE 1024 typedef struct { OVERLAPPED overlapped; SOCKET sock; WSABUF dataBuf; char buffer[BUFFER_SIZE]; DWORD bytesReceived; DWORD bytesSent; } PER_IO_DATA; DWORD WINAPI WorkerThread(LPVOID lpParam); int main() { WSADATA wsaData; SOCKET listenSock, clientSock; SOCKADDR_IN serverAddr, clientAddr; HANDLE hCompletionPort; DWORD numOfWorkerThreads; SYSTEM_INFO systemInfo; // Initialize Winsock if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to initialize winsock"); return 1; } // Create I/O completion port hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // Get the number of available worker threads GetSystemInfo(&systemInfo); numOfWorkerThreads = systemInfo.dwNumberOfProcessors * 2; // Create worker threads for (int i = 0; i < numOfWorkerThreads; i++) { HANDLE hThread = CreateThread(NULL, 0, WorkerThread, hCompletionPort, 0, NULL); CloseHandle(hThread); } // Create listening socket listenSock = socket(AF_INET, SOCK_STREAM, 0); // Bind and listen serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); serverAddr.sin_port = htons(8888); bind(listenSock, (SOCKADDR*)&serverAddr, sizeof(serverAddr)); listen(listenSock, SOMAXCONN); // Accept incoming connections while (1) { int clientAddrSize = sizeof(clientAddr); clientSock = accept(listenSock, (SOCKADDR*)&clientAddr, &clientAddrSize); // Associate the client socket with the completion port CreateIoCompletionPort((HANDLE)clientSock, hCompletionPort, (DWORD)clientSock, 0); // Create IO operations structure PER_IO_DATA* perIOData = (PER_IO_DATA*)malloc(sizeof(PER_IO_DATA)); ZeroMemory(perIOData, sizeof(PER_IO_DATA)); perIOData->sock = clientSock; perIOData->dataBuf.len = BUFFER_SIZE; perIOData->dataBuf.buf = perIOData->buffer; // Start asynchronous receiving DWORD bytesReceived; if (WSARecv(clientSock, &(perIOData->dataBuf), 1, &bytesReceived, &(perIOData->flags), (LPOVERLAPPED)perIOData, NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("Failed to start WSARecv: %d", WSAGetLastError()); closesocket(clientSock); free(perIOData); continue; } } } // Cleanup closesocket(listenSock); WSACleanup(); return 0; } DWORD WINAPI WorkerThread(LPVOID lpParam) { HANDLE hCompletionPort = (HANDLE)lpParam; DWORD bytesTransferred; PER_IO_DATA* perIOData; while (1) { // Get the next completed IO operation GetQueuedCompletionStatus(hCompletionPort, &bytesTransferred, (LPDWORD)&perIOData, (LPOVERLAPPED*)&perIOData, INFINITE); // Handle the completed IO operation if (bytesTransferred == 0) { // Client has disconnected closesocket(perIOData->sock); free(perIOData); continue; } // Process the received data // ... // Start asynchronous sending DWORD bytesSent; perIOData->bytesSent = bytesTransferred; if (WSASend(perIOData->sock, &(perIOData->dataBuf), 1, &bytesSent, 0, (LPOVERLAPPED)perIOData, NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("Failed to start WSASend: %d", WSAGetLastError()); closesocket(perIOData->sock); free(perIOData); continue; } } // Start asynchronous receiving for the next data perIOData->dataBuf.len = BUFFER_SIZE; perIOData->dataBuf.buf = perIOData->buffer; DWORD bytesReceived; if (WSARecv(perIOData->sock, &(perIOData->dataBuf), 1, &bytesReceived, &(perIOData->flags), (LPOVERLAPPED)perIOData, NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("Failed to start WSARecv: %d", WSAGetLastError()); closesocket(perIOData->sock); free(perIOData); continue; } } } return 0; } ``` 这个示例代码实现了一个使用IOCP模型的服务端,它首先初始化Winsock,并创建一个I/O完成端口。然后,它根据系统中可用的线程数量创建相应数量的工作线程。接下来,它创建一个监听套接字,并绑定到指定的端口上,然后开始接受客户端连接。对于每个连接,它会将客户端套接字与完成端口关联,并开始异步接收数据。在工作线程中,它通过GetQueuedCompletionStatus函数获取完成的I/O操作,然后根据操作类型进行相应的处理。在这个例子中,它处理接收到的数据,并通过异步发送响应。然后,它再次开始异步接收数据,以准备接收下一次请求。 这段代码基于Windows平台,使用了Winsock库来实现网络操作,通过WSARecv和WSASend函数来进行异步IO操作。每个I/O操作都使用了PER_IO_DATA结构体来保存相关的信息,并通过OVERLAPPED结构体进行异步操作的调度。主函数负责创建socket、绑定地址、监听连接等,然后将接受到的连接关联到完成端口并开启异步接收。 总结来说,IOCP模型通过异步IO操作和完成端口机制,能够高效处理并发的网络请求,提供了良好的可扩展性和性能。 ### 回答3: IOCP(Input/Output Completion Port)模型是一种高效的异步I/O处理模型,它在Windows平台上常用于服务端程序的开发。下面是一个用C语言实现的基本IOCP模型服务端代码,代码如下: ```c #include <stdio.h> #include <winsock2.h> #define MAX_CLIENTS 1000 // 定义一个自定义结构体用于保存每个客户端的信息 typedef struct { DWORD dwLastBytesRecv; // 上次接收的数据字节数 SOCKET socket; // 客户端套接字 // ... } CLIENT_INFO; int main() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed.\n"); return 1; } // 创建套接字 SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket == INVALID_SOCKET) { printf("Failed to create socket.\n"); WSACleanup(); return 1; } // 绑定套接字到本地地址和端口 sockaddr_in serverAddress; memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); serverAddress.sin_port = htons(12345); if (bind(listenSocket, (sockaddr*)&serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) { printf("Failed to bind socket.\n"); closesocket(listenSocket); WSACleanup(); return 1; } // 监听套接字 if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) { printf("Failed to listen.\n"); closesocket(listenSocket); WSACleanup(); return 1; } // 创建完成端口 HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (hCompletionPort == NULL) { printf("Failed to create completion port.\n"); closesocket(listenSocket); WSACleanup(); return 1; } // 关联监听套接字和完成端口 if (CreateIoCompletionPort((HANDLE)listenSocket, hCompletionPort, 0, 0) == NULL) { printf("Failed to create completion port association.\n"); CloseHandle(hCompletionPort); closesocket(listenSocket); WSACleanup(); return 1; } printf("Server started.\n"); // 循环接收客户端连接请求 while (1) { // 等待客户端连接 sockaddr_in clientAddress; int clientAddressSize = sizeof(clientAddress); SOCKET clientSocket = accept(listenSocket, (sockaddr*)&clientAddress, &clientAddressSize); if (clientSocket == INVALID_SOCKET) { printf("Failed to accept client.\n"); closesocket(listenSocket); CloseHandle(hCompletionPort); WSACleanup(); return 1; } // 将新连接的客户端套接字关联到完成端口 if (CreateIoCompletionPort((HANDLE)clientSocket, hCompletionPort, 0, 0) == NULL) { printf("Failed to create completion port association for client.\n"); closesocket(clientSocket); closesocket(listenSocket); CloseHandle(hCompletionPort); WSACleanup(); return 1; } // 创建客户端信息结构体并初始化 CLIENT_INFO* clientInfo = (CLIENT_INFO*)malloc(sizeof(CLIENT_INFO)); if (clientInfo == NULL) { printf("Failed to allocate memory for client info.\n"); closesocket(clientSocket); closesocket(listenSocket); CloseHandle(hCompletionPort); WSACleanup(); return 1; } clientInfo->dwLastBytesRecv = 0; clientInfo->socket = clientSocket; // 启动异步接收操作 DWORD dwBytesRecv; WSABUF dataBuff; dataBuff.buf = NULL; dataBuff.len = 0; DWORD dwFlags = 0; if (WSARecv(clientSocket, &dataBuff, 1, &dwBytesRecv, &dwFlags, NULL, NULL) == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { printf("Failed to start asynchronous receive.\n"); free(clientInfo); closesocket(clientSocket); closesocket(listenSocket); CloseHandle(hCompletionPort); WSACleanup(); return 1; } printf("New client connected.\n"); } // 清理资源 closesocket(listenSocket); CloseHandle(hCompletionPort); WSACleanup(); return 0; } ``` 这段代码实现了使用IOCP模型的服务端程序,包括创建监听套接字、绑定端口、监听连接、创建完成端口等操作。在接收到客户端连接请求后,将客户端套接字关联到完成端口上,并通过异步I/O的方式进行数据接收操作。 这只是一个基本的示例代码,实际应用中可能会有更多的功能和错误处理。需要注意的是,在使用这段代码时,需要在编译选项中加入 `-lws2_32` 参数,以链接 Windows Sockets 2 库。 希望以上回答对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值