winsock IO 模型---重叠IO之事件通知 example code

来源:互联网 发布:java如何显示行数 编辑:程序博客网 时间:2024/03/29 03:12

// OverlappedIO.cpp  IO重叠之事件通知example cpp

#include "stdafx.h"  
using std::map; 

#define DATA_BUFSIZE 4096   //数据缓冲区的大小
#define _ENTER_CS_CLT EnterCriticalSection(&g_CSListClients)    //加锁  
#define _LEAVE_CS_CLT LeaveCriticalSection(&g_CSListClients)   //解锁


DWORD g_udwIndexEvent = 0;    //事件索引

CRITICAL_SECTION g_CSListClients;   // 临界区

HANDLE g_hThreadServer;   //服务线程

HANDLE g_hEventConn;      //事件


typedef struct MapClientNode  
{  
 SOCKET          sock;    // 句柄
 WSAEVENT        tEvent;   //事件
 DWORD           udwRecvBytes;   //接收到的内容的大小
 WSABUF          tBuf;   // 接收的缓冲区
 WSAOVERLAPPED   tOverLp;   // 重叠IO结构
 bool            bHasHandled;   // 是否己处理
}_CLIENT; 


typedef map<WSAEVENT, _CLIENT> MAP_CLIENT;  

MAP_CLIENT g_mapClient;  


int WriteToArray(MAP_CLIENT::iterator begin, MAP_CLIENT::iterator end, WSAEVENT *aEvents)  
{  
 int c = 0;  
 for (; begin != end; ++begin)  
 {  
  if (false == begin->second.bHasHandled)   //若未处理,则将事件加入事件数组
  {  
   aEvents[c++] = begin->second.tEvent;  
  }  
 }  
 return c;  
}  
//工作线程  
unsigned int __stdcall ServerProc(void *pvParam)  
{  
 DWORD udwFlags = 0;  
 MAP_CLIENT::iterator iter;  
 WSAEVENT aEvents[WSA_MAXIMUM_WAIT_EVENTS];  
 for (;;)  
 {  
  WaitForSingleObject(g_hEventConn, INFINITE);  
  _ENTER_CS_CLT;  

  //这个是主要部分,对每个需要投递WSARecv的连接投递一次  
  for (iter = g_mapClient.begin(); iter != g_mapClient.end();)  
  {  
   if (iter->second.bHasHandled && (WSARecv(iter->second.sock, &iter->second.tBuf, 1, &iter->second.udwRecvBytes,  
    &udwFlags, &iter->second.tOverLp, NULL) == SOCKET_ERROR))  
   {  
    iter->second.bHasHandled = false;  
    switch (WSAGetLastError())  
    {  
    case WSAENOBUFS:  
     ++iter;  
     break;  
    case WSA_IO_PENDING:  
     ++iter;  
     break;  
    default:  
     closesocket(iter->second.sock);  
     WSACloseEvent(iter->second.tEvent);  
     g_mapClient.erase(iter++);  
     break;  
    }  
   }  
   else 
   {  
    ++iter;  
   }  
  }  
  //把完成投递但是没被处理的事件写到一个缓冲区中  
  int c = WriteToArray(g_mapClient.begin(), g_mapClient.end(), aEvents);  
  //等待其中一个的完成,如果超时就返回再重复  
  DWORD dwIndex = WSAWaitForMultipleEvents(c, aEvents, false, 100, false);  
  if (WSA_WAIT_TIMEOUT == dwIndex)  
  {  
   _LEAVE_CS_CLT;  
   continue;  
  }  
  dwIndex -= WSA_WAIT_EVENT_0;  
  WSAEVENT tEvent = aEvents[dwIndex];  
  WSAResetEvent(tEvent);  
  //解析事件,读取报文  
  _CLIENT *ptClient = &g_mapClient[tEvent];  
  WSAGetOverlappedResult(ptClient->sock, &ptClient->tOverLp, &ptClient->udwRecvBytes, false, &udwFlags);  
  ptClient->bHasHandled = true;  
  if (0 == ptClient->udwRecvBytes)  
  {  
   closesocket(ptClient->sock);  
   WSACloseEvent(tEvent);  
   delete[] ptClient->tBuf.buf;  
   g_mapClient.erase(tEvent);  
   printf("connection closed/n");  
   if (g_mapClient.empty())  
   {  
    printf("no connection/n");  
    ResetEvent(g_hEventConn);  
   }  
  }  
  else 
  {  
   ptClient->tBuf.buf[ptClient->udwRecvBytes] = 0;  
   printf("%s/n", ptClient->tBuf.buf);  
   ZeroMemory(&ptClient->tOverLp, sizeof(WSAOVERLAPPED));  
   ptClient->tOverLp.hEvent = ptClient->tEvent;  
  }  
  _LEAVE_CS_CLT;  
 }  
 return 0;  
}


int main(int argc, char* argv[])  
{  
 
 InitializeCriticalSection(&g_CSListClients);  
 g_hEventConn = CreateEvent(NULL, true, false, NULL);  
 
 //初始化工作  
 WSADATA wsaData;  
 if(WSAStartup(MAKEWORD(2,2),&wsaData)!= 0)
 {
  OutputDebugStringA("Error : call function WSAStartup() failed!");
  return -1;
 }
 
 //创建一个TCP套接字,带上WSA_FLAG_OVERLAPPED标志  
 SOCKET ListenSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);  
 if(ListenSocket == INVALID_SOCKET)
 {
  OutputDebugStringA("Error : call function WSASocket() failed !");
  return -2;
 } 
 
 //bind地址
 SOCKADDR_IN ServerAddr;  
 ServerAddr.sin_family = AF_INET;  
 ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;  
 ServerAddr.sin_port = htons(1234);  
 if(SOCKET_ERROR == bind(ListenSocket,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr)))
 {
  OutputDebugStringA("Error : call function bind() failed !");
  return -3;
 }

    //开始监听  
 if(SOCKET_ERROR == listen(ListenSocket,5))
 {
  OutputDebugStringA("Error : call function lister() failed");
  return -4;
 }  
   
 //起一个工作线程,用于处理客户端连接  
 ResetEvent(g_hEventConn);  
 g_hThreadServer = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, NULL);


 //创建Overlapped 结构变量
 WSAOVERLAPPED AcceptOverlapped;

 //创建用于AcceptEx的事件对象   
 WSAEVENT hWsaEventAccept = WSACreateEvent();

 //创建用于AcceptEx的缓冲区
 unsigned char ucBuf[256] = {0};   

 for (;;)  
 {  
  DWORD udwRecv = 0;

  ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));  
  AcceptOverlapped.hEvent = hWsaEventAccept;

  //创建一个新的套接字用于AcceptEx,因为AcceptEx本身不创建套接字  
  SOCKET AcceptSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);


  //传递一个overlapped结构,这样就就可以用事件对象来通知  
  AcceptEx(ListenSocket, AcceptSocket, ucBuf, 0, sizeof(SOCKADDR_IN) + 16,  
   sizeof(SOCKADDR_IN) + 16, &udwRecv, &AcceptOverlapped); 


  //等待连接事件的发生,待事件被通知时记得要重置  
  DWORD udwWaitRet = WSAWaitForMultipleEvents(1, &hWsaEventAccept, false, WSA_INFINITE, false);  
  WSAResetEvent(hWsaEventAccept);  

  int dwLocal, dwRemote;  
  sockaddr_in *ptLocalAddr;  
  sockaddr_in *ptRemoteAddr;   
  //解析缓冲区读取远程地址  
  GetAcceptExSockaddrs(ucBuf, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16,  
   (sockaddr**)&ptLocalAddr, &dwLocal, (sockaddr**)&ptRemoteAddr, &dwRemote);  
  printf("new connection %s-%u/n", inet_ntoa(ptLocalAddr->sin_addr), ntohs(ptRemoteAddr->sin_port));  

  _CLIENT tClient;  
  //填写这个新连接的数据结构字段,包括套接字,缓冲区和事件对象  
  tClient.tEvent          = WSACreateEvent();  
  tClient.sock            = AcceptSocket;  
  tClient.tBuf.buf        = new char[DATA_BUFSIZE];  
  tClient.tBuf.len        = DATA_BUFSIZE;  
  ZeroMemory(&tClient.tOverLp.hEvent, sizeof(WSAOVERLAPPED));  
  tClient.tOverLp.hEvent  = tClient.tEvent;  
  tClient.bHasHandled     = true;  
  _ENTER_CS_CLT;  
  //如果已经有64个连接,那么就报错  
  if (g_mapClient.size() == 64)  
  {  
   closesocket(AcceptSocket);  
   printf("too much connection/n");  
   continue;  
  }  
  //插入到工作线程的连接表中  
  g_mapClient.insert(MAP_CLIENT::value_type(tClient.tEvent, tClient));  
  if (1 == g_mapClient.size())  
  {  
   SetEvent(g_hEventConn);  
  }  
  _LEAVE_CS_CLT;  
 }  
 CloseHandle(g_hEventConn);  
 CloseHandle(g_hThreadServer);  
 DeleteCriticalSection(&g_CSListClients);  
 return 0;  
}

原创粉丝点击