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时,“还有很多要注意的细节与应用场合相关”。
- socket 五种模型中 WSAWaitForMultipleEvents
- WSAWaitForMultipleEvents()
- WSAWaitForMultipleEvents
- WSAWaitForMultipleEvents() .
- WSAWaitForMultipleEvents
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- Windows Socket五种I/O模型
- 设置grub2密码
- hdu3746(KMP)
- 设计模式六大原则:开闭原则
- Genero前世今生
- 【热门主题:动漫进击巨人xp主题】
- socket 五种模型中 WSAWaitForMultipleEvents
- .net通过获取客户端IP地址反查出用户的计算机名
- jsp 获取系统时间并赋值给value
- 软件开发周期延期浅谈
- 楼梯有m层,可以迈1步或两步,有几种方法?
- ffmpeg中的sws_scale算法性能测试
- 一段很经典话
- 现代Web应用开发者必备的六大技能
- 【最难得的,是叫一个执着的人学会放下】