当一个FD_XXX网络事件发生时,WSAEventSelect()函数将导致一个应用程序指定的事件对象被设置,将网络事件投递到一个事件对象句柄。
  传输服务提供者会记住每个特定的FD_XXX网络事件的发生。应用程序可以调用WSAEnumNetworkEvents()函数把目前的网络事件记忆拷贝到应用程序提供的缓冲区中,并且自动清除网络事件记忆。如果需要,应用程序还可以把某个特定的时间对象和网络事件一起清除。
 
源代码如下
一、客户端
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFFER_SIZE 1024
int main(int argc, char **argv)
{
  WSADATA wsaData;
  sockaddr_in ser;
  
  SOCKET sClient;
  
  char send_buf[] = "hello, I am a client";
  
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    printf("WSAStartup()/n");
    return 0;
  }
  
  ser.sin_family = AF_INET;
  ser.sin_port = htons(5050);
  ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  
  sClient = socket(AF_INET, SOCK_STREAM, 0);
  if (sClient == INVALID_SOCKET)
  {
    printf("socket()/n");
    return 0;
  }
  if (connect(sClient, (sockaddr*)&ser, sizeof(ser)) == INVALID_SOCKET)
  {
    printf("socket()/n");
    return 0;
  }
  else
  {
    for (int i = 0; i < 10; i++)
    {
      int iLen = send(sClient, send_buf, sizeof(send_buf), 0);
      if (iLen == 0)
      {
        return 0;
      }
      else if (iLen == SOCKET_ERROR)
      {
        printf("send()/n");
        return 0;
      }
    }
  }
  closesocket(sClient);
  WSACleanup();
  return 0;
}
二、服务器端
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFFER_SIZE 1024
void CompressArrays(WSAEVENT events[], SOCKET sockets[], DWORD *total, int index)
{
  for (size_t i = index + 1; i < *total; i++)
  {
    events[i - 1] = events[i];
  }
  *total--;
}
int main(int argc, char **argv)
{
  WSADATA wsaData;
  char buffer[BUFFER_SIZE];
  sockaddr_in InternetAddr;
  SOCKET SocketArray[WSA_MAXIMUM_WAIT_EVENTS];
  
  WSANETWORKEVENTS NetworkEvents;
  WSAEVENT NewEvent;
  WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
  
  SOCKET Accept, Listen;
  
  DWORD EventTotal = 0;
  DWORD Index;
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    printf("WSAStartup()/n");
    return 0;
  }
  // 创建一个流式套接口
  Listen = socket(AF_INET, SOCK_STREAM, 0);
  InternetAddr.sin_family = AF_INET;
  InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  InternetAddr.sin_port = htons(5050);
  if (bind(Listen, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
  {
    printf("bind()/n");
    return 0;
  }
  // 创建一个事件对象
  NewEvent = WSACreateEvent();
  // 在Listen套接口上注册套接口连接和关闭的网络事件
  WSAEventSelect(Listen, NewEvent, FD_ACCEPT | FD_CLOSE);
  
  if (listen(Listen, 5) == SOCKET_ERROR)
  {
    printf("listen()/n");
    return 0;
  }
  SocketArray[EventTotal] = Listen;
  EventArray[EventTotal] = NewEvent;
  EventTotal++;
  while (true)
  {
    // 在所有套接口上等待网络事件的发生
    Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
    if (WSAEnumNetworkEvents(SocketArray[Index - WSA_WAIT_EVENT_0],
               EventArray[Index - WSA_WAIT_EVENT_0],
               &NetworkEvents) == SOCKET_ERROR)
    {
      printf("%d/n", WSAGetLastError());
      printf("WSAEnumNetworkEvents()/n");
      return 0;
    }
               
    // 检查FD_ACCEPT
    if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
    {
      if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
      {
        WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
        printf("FD_ACCEPT failed with error %d/n", NetworkEvents.iErrorCode[FD_ACCEPT_BIT]);
        break;
      }
      // 接收新的连接,并将其存入套接口数组
      Accept = accept(SocketArray[Index - WSA_WAIT_EVENT_0], NULL, NULL);
      // 当套接口的数量超界时,关闭该套接口
      if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
      {
        printf("Too many connections");
        closesocket(Accept);
        break;
      }
      NewEvent = WSACreateEvent();
      if (NewEvent == WSA_INVALID_EVENT)
      {
        printf("WSACreateEvent()/n");
        break;
      }
      WSAEventSelect(Accept, NewEvent, FD_READ | FD_WRITE | FD_CLOSE);
      EventArray[EventTotal] = NewEvent;
      SocketArray[EventTotal] = Accept;
      EventTotal++;
      printf("Socket %d connected/n", Accept);
    }
    
    // 一下处理FD_READ通知
    if (NetworkEvents.lNetworkEvents & FD_READ)
    {
      if (NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
      {
        WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
        printf("FD_READ failed with error %d/n", NetworkEvents.iErrorCode[FD_READ_BIT]);
        break;
      }
      
      // 从套接口读入数据
      int iRecv = recv(SocketArray[Index - WSA_WAIT_EVENT_0], buffer, sizeof(buffer), 0);
      if (iRecv == 0)
      {
        break;
      }
      else if (iRecv == SOCKET_ERROR)
      {
        printf("recv()/n");
        break;
      }
      else
      {
        printf("recv data: %s", buffer);
      }
    }
    
    // 以下处理FD_WRITE通知
    if (NetworkEvents.lNetworkEvents & FD_WRITE)
    {
      if (NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0)
      {
        WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
        printf("FD_WRITE failed with error %d/n", NetworkEvents.iErrorCode[FD_WRITE_BIT]);
        break;
      }
      send(SocketArray[Index - WSA_WAIT_EVENT_0], buffer, sizeof(buffer), 0);
    }
    
    // 以下处理FD_CLOSE通知
    if (NetworkEvents.lNetworkEvents & FD_CLOSE)
    {
      if (NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0)
      {
        WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
        printf("FD_WRITE faield with error %d/n", NetworkEvents.iErrorCode[FD_WRITE_BIT]);
        break;
      }
      
      // 关闭套接口
      closesocket(SocketArray[Index - WSA_WAIT_EVENT_0]);
      // 从套接口事件和事件数组中删除关闭的套接口的有关信息
      CompressArrays(EventArray, SocketArray, &EventTotal, Index - WSA_WAIT_EVENT_0);
    }
  }
  WSACleanup();
  return 0;
}