socket 五种模型中 WSAWaitForMultipleEvents

来源:互联网 发布:北京 考研 机构 知乎 编辑:程序博客网 时间:2024/03/29 19:53

WaitForMultipleObjects

多个内核对象被触发时,WaitForMultipleObjects选择其中序号最小的返回。而WaitForMultipleObjects它只会改变使它返回的那个内核对象的状态。
这儿又会产生一个问题,如果序号最小的那个对象频繁被触发,那么序号比它大的内核对象将得不到被处理的机会。
为了解决这一问题,可以采用双WaitForMultipleObjects检测机制来实现。

一下是某书中例子:

#include "stdafx.h"#include <Winsock2.h>#include <malloc.h>#pragma comment(lib,"Ws2_32.lib")void InitSock(){ WORD wVersionRequested; WSADATA wsaData; int err;   wVersionRequested = MAKEWORD( 2, 2 );   err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) {  /* Tell the user that we could not find a usable */  /* WinSock DLL.                                  */  return; }   /* Confirm that the WinSock DLL supports 2.2.        */ /* Note that if the DLL supports versions greater    */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we      */ /* requested.                                        */   if ( LOBYTE( wsaData.wVersion ) != 2 ||   HIBYTE( wsaData.wVersion ) != 2 ) {  /* Tell the user that we could not find a usable */  /* WinSock DLL.                                  */  WSACleanup( );  return;  }}//套接字对象typedef struct _SOCKET_OBJ{ SOCKET s;  //套接字句柄 HANDLE hEvent;  //与套接字相关联的事件对象句柄 sockaddr_in addrRemote;  //客户端地址信息 _SOCKET_OBJ *pNext;  //指向一个SOCKET_OBJ}SOCKET_OBJ,*pSOCKET_OBJ;//线程对象typedef struct _THREAD_OBJ{ HANDLE hEvents[WSA_MAXIMUM_WAIT_EVENTS];  //记录当前线程要等待的事件对象的句柄 int nSocketCount;       //记录当前线程处理的套接字数量 pSOCKET_OBJ pSockHeader;   //当前线程处理的套接字对象列表的头指针 pSOCKET_OBJ pSockTail;   //当前线程处理的套接字对象列表的尾指针 CRITICAL_SECTION cs;   //关键代码段变量,为的是同步对本结构的访问 _THREAD_OBJ* pNext;    //指向下一个THREAD_OBJ}THREAD_OBJ,*pTHREAD_OBJ;//申请一个套接字对象pSOCKET_OBJ GetSocketObj(SOCKET s){ pSOCKET_OBJ pSocket=(pSOCKET_OBJ)malloc(sizeof(SOCKET_OBJ)); if(pSocket!=NULL) {  pSocket->hEvent=WSACreateEvent();  pSocket->s=s;  pSocket->pNext=NULL; } return pSocket;}//释放一个套接字对象void FreeSocketObj(pSOCKET_OBJ pSocket){ ::CloseHandle(pSocket->hEvent); if(pSocket->s!=INVALID_SOCKET) {  ::closesocket(pSocket->s); } free(pSocket); pSocket=NULL;}pTHREAD_OBJ g_pThreadList=NULL;//指向线程对象列表的表头CRITICAL_SECTION g_cs;//同步对全局变量的访问//申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中pTHREAD_OBJ GetThreadObj(){ pTHREAD_OBJ pThread=(pTHREAD_OBJ)malloc(sizeof(THREAD_OBJ)); if(pThread!=NULL) {  pThread->pNext=NULL;  pThread->nSocketCount=0;  pThread->pSockHeader=pThread->pSockTail=NULL;  ::InitializeCriticalSection(&pThread->cs);  //创建一个事件对象,用于指示该线程的句柄数组需要重组  pThread->hEvents[0]=WSACreateEvent();  //将线程对象加入线程对象列表中  ::EnterCriticalSection(&g_cs);  pThread->pNext=g_pThreadList;  g_pThreadList=pThread;  ::LeaveCriticalSection(&g_cs); } return pThread;}//释放一个线程对象void FreeThreadObj(pTHREAD_OBJ pThread){ ::EnterCriticalSection(&g_cs); pTHREAD_OBJ p=g_pThreadList; if(p==pThread) {  g_pThreadList=pThread->pNext; } else {  while(p!=NULL&&p->pNext!=pThread)   //找到pThread的前一个节点  {   p=p->pNext;  }  if(p!=NULL)  {   p->pNext=pThread->pNext;  } } ::LeaveCriticalSection(&g_cs); //释放资源 ::CloseHandle(pThread->hEvents[0]); ::DeleteCriticalSection(&pThread->cs); ::free(pThread);}//重新建立线程对象的events数组  即将pSocket中的event与pThread中的events关联起来void RebuildArray(pTHREAD_OBJ pThread){ ::EnterCriticalSection(&pThread->cs); pSOCKET_OBJ pSocket=pThread->pSockHeader; int n=1; //从下标为1开始 while(pSocket!=NULL) {  pThread->hEvents[n++]=pSocket->hEvent;  pSocket=pSocket->pNext; } ::LeaveCriticalSection(&pThread->cs);}LONG g_nTotalConnections;//总共连接数量LONG g_nCurrentConnections;//当前连接数量//向线程的套接字列表中插入一个套接字对象BOOL InsertSocketObj(pTHREAD_OBJ pThread,pSOCKET_OBJ pSocket){ BOOL bRet=false; ::EnterCriticalSection(&pThread->cs); if(pThread->nSocketCount<4-1) {  if(pThread->pSockHeader==NULL)  {   pThread->pSockHeader=pThread->pSockTail=pSocket;  }  else  {   pThread->pSockTail->pNext=pSocket;   pThread->pSockTail=pSocket;  }  pThread->nSocketCount++;  bRet=true; } ::LeaveCriticalSection(&pThread->cs); if(bRet) {  InterlockedIncrement(&g_nTotalConnections);  InterlockedIncrement(&g_nCurrentConnections); } return bRet;} //从给定线程的套接字对象列表中移除一个套接字对象void RemoveSocketObj(pTHREAD_OBJ pThread,pSOCKET_OBJ pSocket){ ::EnterCriticalSection(&pThread->cs); //在套接字对象列表中查找指定的套接字对象,找到后将它移除 pSOCKET_OBJ pTemp=pThread->pSockHeader; if(pTemp==pSocket) {  if(pThread->pSockHeader==pThread->pSockTail)  {   pThread->pSockHeader=pThread->pSockTail=pTemp->pNext;  }  else  {   pThread->pSockHeader=pTemp->pNext;  } } else {  while(pTemp!=NULL&&pTemp->pNext!=pSocket)  {   pTemp=pTemp->pNext;  }  if(pTemp!=NULL)  {   if(pThread->pSockTail==pSocket)   {    pThread->pSockTail=pTemp;   }   pTemp->pNext=pSocket->pNext;  } } pThread->nSocketCount--; ::LeaveCriticalSection(&pThread->cs); ::WSASetEvent(pThread->hEvents[0]); //指示线程重建句柄数组 ::InterlockedDecrement(&g_nCurrentConnections);}pSOCKET_OBJ FindSocketObj(pTHREAD_OBJ pThread,int nIndex){ pSOCKET_OBJ pSocket=pThread->pSockHeader; while(--nIndex) {  if(pSocket==NULL)  {   return NULL;  }  pSocket=pSocket->pNext; } return pSocket;}BOOL HandleIO(pTHREAD_OBJ pThread,pSOCKET_OBJ pSocket){ ::WSANETWORKEVENTS wsaEvent; ::WSAEnumNetworkEvents(pSocket->s,pSocket->hEvent,&wsaEvent); if(wsaEvent.lNetworkEvents&FD_READ) {  if(wsaEvent.iErrorCode[FD_READ_BIT]==0)  {   char *buf=new char[1024];   __try   {        int nRec=recv(pSocket->s,buf,1024,0);    if(nRec>0)    {     buf[nRec]='/0';     printf("收到数据:%s/n",buf);     return true;    }    else    {     return false;    }   }   __finally   {    delete[]buf;   }  } } else if(wsaEvent.lNetworkEvents&FD_CLOSE) {  printf("一连接继开/n");  RemoveSocketObj(pThread,pSocket);  FreeSocketObj(pSocket);  return false; } return true;}//处理I/O的线程DWORD WINAPI ServerThread(LPVOID lpParam){ pTHREAD_OBJ pThread=(pTHREAD_OBJ)lpParam; printf("新线程%d起动/n",GetCurrentThreadId()); while(true) {  //等待网络事件  int nIndex=WSAWaitForMultipleEvents(pThread->nSocketCount+1,pThread->hEvents,false,WSA_INFINITE,false);  nIndex=nIndex-WSA_WAIT_EVENT_0;  //查看受信事件  for(int i=nIndex;i<pThread->nSocketCount+1;i++)  {   nIndex=WSAWaitForMultipleEvents(1,&pThread->hEvents[i],true,0,false);   if(WSA_WAIT_FAILED==nIndex||WSA_WAIT_TIMEOUT==nIndex)   {    continue;   }   else   {    if(0==i)    {     RebuildArray(pThread);     if(pThread->nSocketCount==0)     {      FreeThreadObj(pThread);      printf("线程%d退出/n",GetCurrentThreadId());      return 0;     }     ::WSAResetEvent(pThread->hEvents[0]);    }    else    {     pSOCKET_OBJ pSocket=FindSocketObj(pThread,i);     if(pSocket!=NULL)     {      if(!HandleIO(pThread,pSocket))      {       RebuildArray(pThread);      }     }     else     {      printf("unable to find socket object/n");     }    }   }  } }}//将一个套接字对象安排给空闲的线程处理void AssignToFreeThread(pSOCKET_OBJ pSocket){ pSocket->pNext=NULL; ::EnterCriticalSection(&g_cs); pTHREAD_OBJ pThread=g_pThreadList; //试图插入到现存的线程 while(pThread!=NULL) {  if(InsertSocketObj(pThread,pSocket))  {   break;  }  pThread=pThread->pNext; } //如果没有空闲线程,为这个套接字对象创建新的线程 if(pThread==NULL) {  pThread=GetThreadObj();  InsertSocketObj(pThread,pSocket);  ::CreateThread(NULL,0,ServerThread,pThread,0,NULL); } ::LeaveCriticalSection(&g_cs); //指示线程重建句柄数组 ::WSASetEvent(pThread->hEvents[0]);}int _tmain(int argc, _TCHAR* argv[]){ InitSock(); SOCKET sListen=socket(AF_INET,SOCK_STREAM,0); sockaddr_in sin; sin.sin_addr.S_un.S_addr=INADDR_ANY; sin.sin_family=AF_INET; sin.sin_port=htons(3456); if(SOCKET_ERROR==bind(sListen,(sockaddr*)&sin,sizeof(sockaddr))) {  printf("绑定失败/n");  ::WSACleanup();  return 0; } listen(sListen,5); ::WSAEVENT wsaEvent=::WSACreateEvent(); ::WSAEventSelect(sListen,wsaEvent,FD_ACCEPT); ::InitializeCriticalSection(&g_cs); while(true) {  int nRet=::WaitForSingleObject(wsaEvent,10000);  if(nRet==WAIT_FAILED)  {   printf("failed wait for single object/n");   break;  }  else if(nRet==WSA_WAIT_TIMEOUT)  {   printf(" TotalConnections:%d/n",g_nTotalConnections);   printf(" TotalCurrentConnections:%d/n",g_nCurrentConnections);  }  else  {   ::ResetEvent(wsaEvent);   while(true)   {    sockaddr_in si;    int nLen=sizeof(si);    SOCKET client=accept(sListen,(sockaddr*)&si,&nLen);    if(client==SOCKET_ERROR)     break;    pSOCKET_OBJ pSocket=GetSocketObj(client);    pSocket->addrRemote=si;    ::WSAEventSelect(pSocket->s,pSocket->hEvent,FD_READ|FD_CLOSE);    AssignToFreeThread(pSocket);    printf("一新连接/n");   }  } } ::DeleteCriticalSection(&g_cs); return 0;} 
应用一个api时,“还有很多要注意的细节与应用场合相关”。

原创粉丝点击