Windows 网络编程(8).4

时间:2022-07-24 23:54:24

4.3  WSAEventSelect
      该模型把网络事件投递至一个事件对象句柄,而不是一个窗口函数
      4.3.1事件通知:
        应用程序针对打算使用的套接字,首先创建一个事件对象
        WSAEVENT    WSACreateEvent( void );
        将返回事件句柄对象与套接字关联在一起,同时注册感兴趣的网络事件
        int  WSAEventSelect( SOCKET  s,
                             WSAEVENT hEventObject,     //事件句柄对象
                             long     lNetworkEvents
                           );
          
         为WSAEventSelect创建的事件有两种状态以及两种工作模式:
         工作状态:己传信和未传信
         工作模式:人工重设和自动重设
         WSACreateEvent最开始用未传信的工作状态和人工重设模式创建事件句柄,随着网络事件触发
         事件对象,工作状态便会从"未传信"转变成"己传信",由于事件对象是人工模式创建的,所在
         程序在完成了一个I/O处理后,应把事件对象从"己传信"更改为"未传信",用WSAResetEvent函数
         BOOL   WSAResetEvent( WSAEVENT  hEvent);
         程序完成对一个事件对象的处理后,应调用WSACloseEvent函数,释放资源
         BOOL   WSACloseEvent (WSAEVENT  hEvent); 
       4.3.2
         套接字与事件对象句柄关联后,程序就应该I/O处理:方法是等待网络事件触发事件对象句柄的
         工作状态,WSAWaitForMulipleEvents函数,便是用来等待一个或多个事件对象句柄,并在事先指
         定的一个或所有句柄进入"己传信"状态后或超时后,立即返回.
         DWORD    WSAWaitForMultipleEvents
          (DWORD    cEvents,      //WSAEVENT对象构成的数组里的事件对象数量
           const WSAEVENT FAR * lphEvents,   //指向事件对象数组
           BOOL  fWaitAll,        //True,所有事件对象变成"己传信"时,返回,否则有一个变就返回
           DWORD  dwTimeout,     //最多一个网络事件发生时,函数的等待时间
           BOOL   fAlertable    //FALSE
          );返回造成函数返回的事件对象的索引,从而知道发生在哪个套接字上
       例:
          Index = WSAWaitForMultipleEvents(....);
          MyEvent = EvnetArray[Index-WSA_WAIT_EVENT_0];
         接下来,调查发生网络事件的类型(WSAEnumNetworkEvents):
         int WSAEnumNetworkEvent(
                  SOCKET     s,         //对应产生网络事件的套接字
                  WSAEVENT   hEventObject,   //准备置成"未传信"状态的事件句柄
                  LPWSANETWORKEVENTS  lpNetworkEvents  //指向 WSANETWORKEVENTS结构,如下:
         );
         typedef  struct _WSANETWORKEVENTS
         {
             long  lNetworkEvents;  //对应套接字上发生的所有网络事件类型(可能多个)
             int   iErrorCode[FD_MAX_EVENTS];   //错误代码数组,同lNetworkEVents关联
                                                //每个错误索引是在事件类型后加'_BIT'
         } WSANETWORKEVENTS,FAR* LPWSANETWORKEVENTS;
        例:
          // Process FD_READ notification
          if (NetworkEvents.lNetworkEvents & FD_READ)
          {
              if(NetworkEvents.iErrorCode[FD_READ_BIT] !=0)
              {
                 //Error
              }
          }
    例:采用WSAEventSelect I/O模型的示范服务器源代码
    SOCKET    Socket[WSA_MAXIMUM_WAIT_EVENTS];
    WSAEVENT   Event[WSA_MAXINUM_WAIT_EVENTS];
    SOCKET    Accept,
              Listen;
    DWORD     EventTotal = 0;
    DWORD     Index;

    //Set up a TCP socket for listening on port 5150
    Listen = socket(PF_INET,SOCK_STREAM,0);
   
    InternetAddr.sin_family      = AF_INET;
    InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    InternetAddr.sin_port        = htons(5150);

    bind(Listen,(PSOCKADDR) &InternetAddr,sizeof(InternetAddr));

    NewEvent = WSACreateEvent();

    WSAEventSelect(Listen,NewEvnet,FD_ACCEPT|FD_CLOSE);

    listen(Listen,5);

    Socket[EventTotal] = Listen;
    Event[EventTotal] = NewEvent;
    EventTotal++;

    while (TRUE)
    {
        //Wait for network events on all sockets
        Index = WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);

        WSAEnumNewWorkEvents(SocketArray[Index-WSA_WAIT_EVENT_0],
                              EventArray[Index-WSA_WAIT_EVENT_0],
                              &NetworkEvents);
        //Check for FD_ACCEPT messages
        if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
        {
           if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] !=0)
           {
              //Error
              break;
           }
           //Accept a new connection and add it to the socket and event lists
           Accept = accept(SocketArray[Index-WSA_WAIT_EVENT_0],NULL,NULL);

           //We cannot process more than WSA_MAXIMUM_WAIT_EVENTS sockets ,
           //so close the accepted socket
           if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
           {
               printf("........");
               closesocket (Accept);
               break;
           }
           NewEvent = WSACreateEvent();
      
           WSAEventSelect(Accept,NewEvent,FD_READ|FD_WRITE|FD_CLOSE);

           Event[EventTotal] = NewEvent;
           Socket[EventTotal]= Accept;
           EventTotal++;
           prinrt("Socket %d connect/n",Accept);
        }
        //Process FD_READ notification
        if (NetworkEvents.lNetworkEvents & FD_READ)
        {
            if (NetworkEvents.iErrorCode[FD_READ_BIT !=0])
            {
               //Error
               break;
            }

            //Read data from the socket
            recv(Socket[Index-WSA_WAIT_EVENT_0],buffer,sizeof(buffer),0);
        }
        //process FD_WRITE notitication
        if (NetworkEvents.lNetworkEvents & FD_WRITE)
        {
            if (NetworkEvents.iErrorCode[FD_WRITE_BIT] !=0)
            {
               //Error
               break;
            }
            send(Socket[Index-WSA_WAIT_EVENT_0],buffer,sizeof(buffer),0);
        }
        if (NetworkEvents.lNetworkEvents & FD_CLOSE)
        {
            if(NetworkEvents.iErrorCode[FD_CLOSE_BIT] !=0)
            {
                //Error
                break;
            }
            closesocket (Socket[Index-WSA_WAIT_EVENT_0]);
            //Remove socket and associated event from the Socket and Event arrays and
            //decrement eventTotal
            CompressArrays(Event,Socket,& EventTotal);
        }
    }