Socket I/O模型之事件选择(WSAEventSelect)

来源:互联网 发布:阿里云 全民云计算 编辑:程序博客网 时间:2024/05/16 16:58
WSAEventSelect和WSAAsyncSelect模型类似,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知。对于WSAAsyncSelect模型采用的网络事件来说,它们均可原封不动地移植到事件选择模型上。在用事件选择模型开发的应用程序中,也能接收和处理所有那些事件。该模型最主要的差别在于网络事件会投递至一个事件对象句柄,而非投递至一个窗口例程。

服务器端代码:
C++代码  收藏代码
  1. // write by larry  
  2. // 2009-8-20  
  3. // This is a server using WSAEventSelect model.  
  4. #include "stdafx.h"  
  5. #include <WINSOCK2.H>  
  6. #include <stdio.h>  
  7. #pragma comment(lib, "ws2_32.lib")  
  8. #define PORT  5150  
  9. #define MSGSIZE  1024  
  10. int g_iTotalConn = 0;  
  11. SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];  
  12. WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];  
  13. DWORD WINAPI WorkerThread(LPVOID lpParam);  
  14. void Cleanup(int index);  
  15. int main(int argc, char* argv[])  
  16. {  
  17.     WSADATA wsaData;  
  18.     SOCKET sListen, sClient;  
  19.     SOCKADDR_IN local, client;  
  20.     DWORD dwThreadId;  
  21.     int iAddrSize = sizeof(SOCKADDR_IN);  
  22.     // Initialize windows socket library  
  23.     WSAStartup(0x0202, &wsaData);  
  24.     // Create listening socket  
  25.     sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  26.     // Bind  
  27.     local.sin_family = AF_INET;  
  28.     local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
  29.     local.sin_port = htons(PORT);  
  30.     bind(sListen, (sockaddr*)&local, sizeof(SOCKADDR_IN));  
  31.     // Listen  
  32.     listen(sListen, 3);  
  33.     // Create worker thread  
  34.     CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);  
  35.     while (TRUE)  
  36.     {  
  37.         // Accept a connection  
  38.         sClient = accept(sListen, (sockaddr*)&client, &iAddrSize);  
  39.         printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));  
  40.         // Associate socket with network event  
  41.         g_CliSocketArr[g_iTotalConn] = sClient;  
  42.         g_CliEventArr[g_iTotalConn] = WSACreateEvent();  
  43.         WSAEventSelect(g_CliSocketArr[g_iTotalConn], g_CliEventArr[g_iTotalConn], FD_READ|FD_CLOSE);  
  44.         g_iTotalConn++;  
  45.     }  
  46.     return 0;  
  47. }  
  48. DWORD WINAPI WorkerThread(LPVOID lpParam)  
  49. {  
  50.     int ret, index;  
  51.     WSANETWORKEVENTS NetworkEvents;  
  52.     char szMessage[MSGSIZE];  
  53.     while (TRUE)  
  54.     {  
  55.         ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);  
  56.         if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)  
  57.         {  
  58.             continue;  
  59.         }  
  60.         index = ret - WSA_WAIT_EVENT_0;  
  61.         WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index], &NetworkEvents);  
  62.         if (NetworkEvents.lNetworkEvents & FD_READ)  
  63.         {  
  64.             // Receive message from client  
  65.             ret = recv(g_CliSocketArr[index], szMessage, MSGSIZE, 0);  
  66.             if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))  
  67.             {  
  68.                 Cleanup(index);  
  69.             }  
  70.             else  
  71.             {  
  72.                 szMessage[ret] = '\0';  
  73.                 send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);  
  74.             }  
  75.         }  
  76.         if (NetworkEvents.lNetworkEvents & FD_CLOSE)  
  77.         {  
  78.             Cleanup(index);  
  79.         }  
  80.     }  
  81.     return 0;  
  82. }  
  83. void Cleanup(int index)  
  84. {  
  85.     closesocket(g_CliSocketArr[index]);  
  86.     WSACloseEvent(g_CliEventArr[index]);  
  87.     if (index < g_iTotalConn-1)  
  88.     {  
  89.         g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn-1];  
  90.         g_CliEventArr[index] = g_CliEventArr[g_iTotalConn-1];  
  91.     }  
  92.     g_iTotalConn--;  
  93. }  

事件选择模型也比较简单,实现起来也不是太复杂,它的基本思想是将每个套接字都和一个WSAEVENT对象对应起来,并且在关联的时候指定需要关注的哪些网络事件。一旦在某个套接字上发生了我们关注的事件(FD_READ和FD_CLOSE),与之相关联的WSAEVENT对象被Signaled。程序定义了两个全局数组,一个套接字数组,一个WSAEVENT对象数组,其大小都是MAXIMUM_WAIT_OBJECTS(64),两个数组中的元素一一对应。

同样的,这里的程序没有考虑两个问题,一是不能无条件的调用accept,因为我们支持的并发连接数有限。解决方法是将套接字按 MAXIMUM_WAIT_OBJECTS分组,每MAXIMUM_WAIT_OBJECTS个套接字一组,每一组分配一个工作者线程;或者采用 WSAAccept代替accept,并回调自己定义的Condition Function。第二个问题是没有对连接数为0的情形做特殊处理,程序在连接数为0的时候CPU占用率为100%。