WSAEventSelect模型

来源:互联网 发布:编程教育 编辑:程序博客网 时间:2024/05/16 04:41

     WSAEventSelect模型类似于WSAAsynSelect模型,但最主要的区别是网络事件发生时会被发送到一个事件对象句柄,而不是发送到一个窗口。该模式下编程步骤为:

【1】首先创建事件对象来接收网络事件。

       WSAEVENT         WSACreateEvent(void);

       调用后返回事件对象句柄,初始状态为无信号且为手工重置事件,如果程序想使用自动重置事件则应使用CreateEvent()函数。

【2】注册网络事件

       int    WSAEventSelect(SOCKET s,WSAEVENT   hEventObject,long  lNetworkEvents);

       此函数将事件对象hEventObject与套接字相关联,同时注册网络事件lNetworkEvent,当s上注册的网络事件发生时,对应的事件对象将收到信号。

【3】等待网络事件来触发事件句柄的状态

      DWORD  WSAWaitForMultipleEvent(DWORD  cEvents,const WSAEVENT FAR* lphEvents,

bool  fWaitAll,DWORD  dwTimeout,BOOL  fAlertable);

      cEvents为事件句柄的数目,其最大值为WSA_MAXIMUM_WAIT_EVENTS,lphEvents为事件句柄数组的指针,fWaitAll指定等待类型,TRUE表示当lphEvents数组中所有事件对象都收到信号后才返回,FALSE表示只要任一事件对象收到信号就返回。dwTimeout为等待的超时时间(单位为毫秒)。fAlertable为指定函数返回时是否执行完成例程。对事件数组中的时间进行引用时,应该用WSAWaitForMutipleEvents()函数的返回值减去预声明值WSA_WAIT_EVENT_0作为数组下标,从而得到具体的引用值。

【4】判断网络事件类型

      int  WSAEnumNetworkEvents(SOCKET s,WSAEVENT  hEventObject,LPWSANETWORKEVENTS  lpNetworkEvents);

      该函数用来指出在套接字s上发生的网络事件。参数s为套接字号,hEventObject是可选参数,用来指定需要重设的事件对象,lpNetworkEvents指向LPWSANETWORKEVENTS 结构的指针,该结构用于记录发生的网络事件和相关的错误代码。

【5】I/O处理后,应重置事件对象为无信号状态

     BOOL   WSAResetEvent(WSAEVENT hEvent);

【6】最后关闭事件对象句柄

实例代码:

#include <winsock2.h>
#include <stdio.h>

#define PORT     5150
#define MSGSIZE 1024

#pragma comment(lib, "ws2_32.lib")

int       g_iTotalConn = 0;
SOCKET    g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];

DWORD WINAPI WorkerThread(LPVOID);
void Cleanup(int index);

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));

     // Associate socket with network event
     g_CliSocketArr[g_iTotalConn] = sClient;
     g_CliEventArr[g_iTotalConn] = WSACreateEvent();
     WSAEventSelect(g_CliSocketArr[g_iTotalConn],
                    g_CliEventArr[g_iTotalConn],
                    FD_READ | FD_CLOSE);

     g_iTotalConn++;
   }
}

DWORD WINAPI WorkerThread(LPVOID lpParam)
{
   int               ret, index;
   WSANETWORKEVENTS NetworkEvents;
   char              szMessage[MSGSIZE];

   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;
     WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index], &NetworkEvents);

     if (NetworkEvents.lNetworkEvents & FD_READ)
     {
       // Receive message from client
       ret = recv(g_CliSocketArr[index], szMessage, MSGSIZE, 0);
       if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
       {
         Cleanup(index);
       }
       else
       {
         szMessage[ret] = '/0';
         send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);
       }
     }

     if (NetworkEvents.lNetworkEvents & FD_CLOSE)
   {
    Cleanup(index);
   }
   }
   return 0;
}

void Cleanup(int index)
{
   closesocket(g_CliSocketArr[index]);
WSACloseEvent(g_CliEventArr[index]);

if (index < g_iTotalConn - 1)
{
   g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];
   g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];
}

g_iTotalConn--;
}


0 0
原创粉丝点击