从CSDN的一个xd那里看到这个代码,觉得对WSAEventSelect模型的多线程实现做的非常好,代码行文风格和代码质量都是非常不错。我做了些小的改动学习之用。如果你也感兴趣不妨一起留言讨论讨论。 // WSAEventSelect_MT.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <Winsock2.h> #include <malloc.h> #pragma comment(lib,"Ws2_32.lib") /* WSAEventSelect IO模型服务器实现 ----------------------------------------- 1.多线程实现,以支持更多的客户端连接 2.多线程共享同一个监听套接字socketListen 3.IO业务逻辑模块单独实现 4.各个线程各自管理自己的socket客户连接 */ const int MAX_SOCKET_COUNT_PER_THREAD = WSA_MAXIMUM_WAIT_EVENTS - 1; // 定义每个线程最大管理的socket连接数量 // 初始化socket 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 ST_SOCKET_OBJ { SOCKET s; //套接字句柄 HANDLE hEvent; //与套接字s相关联的事件对象句柄 sockaddr_in addrRemote; //客户端地址信息 ST_SOCKET_OBJ *pNext; //指向一个SOCKET_OBJ }SOCKET_OBJ,*pSOCKET_OBJ; //线程对象 /* 线程参数:工作线程监视的socket对象列表以及每个套接字关联的事件数组 */ typedef struct ST_THREAD_OBJ { HANDLE hEvents[WSA_MAXIMUM_WAIT_EVENTS]; //记录当前线程要等待的事件对象的句柄 int nSocketCount; //记录当前线程处理的套接字数量 pSOCKET_OBJ pSockHeader; //当前线程处理的套接字对象列表的头指针 pSOCKET_OBJ pSockTail; //当前线程处理的套接字对象列表的尾指针 CRITICAL_SECTION cs; //关键代码段变量,为的是同步对本结构的访问 ST_THREAD_OBJ* pNext; //指向下一个THREAD_OBJ }THREAD_OBJ,*pTHREAD_OBJ; //申请一个套接字对象 - 为一个连接的socket申请一个ST_SOCKET_OBJ对象,初始化 pSOCKET_OBJ GetSocketObj(SOCKET s) { pSOCKET_OBJ pSocket= (pSOCKET_OBJ)malloc(sizeof(SOCKET_OBJ)); // new SOCKET_OBJ() if(pSocket!=NULL) { pSocket->hEvent=::WSACreateEvent(); // 创建事件 pSocket->s=s; // 初始化套接字socket pSocket->pNext=NULL; } return pSocket; } //释放一个套接字对象 void FreeSocketObj(pSOCKET_OBJ pSocket) { ::CloseHandle(pSocket->hEvent); if(pSocket->s!=INVALID_SOCKET) { ::closesocket(pSocket->s); } free(pSocket); // delete pSocket; pSocket=NULL; } pTHREAD_OBJ g_pThreadList=NULL; //指向线程对象列表的表头 -- 线程参数列表,和线程一一对应 CRITICAL_SECTION g_cs; //同步对全局变量g_pThreadList的访问 //申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中 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); //创建一个事件对象,用于指示该线程的句柄数组hEvents[]需要重组 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]); // pThread->hEvents[0]为用来指示hEvents[]需要重组的事件 ::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开始 跳过pThread->hEvents[0] 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<MAX_SOCKET_COUNT_PER_THREAD) // 每个线程最多管理MAX_SOCKET_COUNT_PER_THREAD个socket连接 { 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); // FD_READ事件 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; } } } // FD_CLOSE事件 else if(wsaEvent.lNetworkEvents&FD_CLOSE) { printf("一连接继开/n"); RemoveSocketObj(pThread,pSocket); // 需要重建句柄数组 //::WSASetEvent(pThread->hEvents[0]); //指示线程重建句柄数组 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; //if(WSA_WAIT_FAILED==nIndex||WSA_WAIT_TIMEOUT==nIndex) //{ // 失败或者超时 // continue; //} //else //{ // 成功受信 // if(0==nIndex) // pThread->hEvents[0]受信 // { // RebuildArray(pThread); // 重建句柄数组 // if(pThread->nSocketCount==0) // { // 如果该线程连接的socket数量已经为0,说明所有的客户都已经断开连接则线程函数自动退出 // FreeThreadObj(pThread); // 线程退出时候回收线程参数分配的内存 // printf("线程%d退出/n",GetCurrentThreadId()); // return 0; // } // ::WSAResetEvent(pThread->hEvents[0]); // } // else // { // pSOCKET_OBJ pSocket=FindSocketObj(pThread,nIndex); // if(pSocket!=NULL) // { // if(!HandleIO(pThread,pSocket)) // { // RebuildArray(pThread); // 重建句柄数组 // } // } // else // { // printf("unable to find socket object/n"); // } // } //} //查看受信事件 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"); } } } } } // end while() } //将一个套接字对象安排给空闲的线程处理 //如果所有线程接受的socket已满,则新建线程提供服务 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,SOMAXCONN); ::WSAEVENT wsaEvent=::WSACreateEvent(); ::WSAEventSelect(sListen,wsaEvent,FD_ACCEPT); ::InitializeCriticalSection(&g_cs); while(true) { //int nRet=::WaitForSingleObject(wsaEvent,10000); int nRet=::WSAWaitForMultipleEvents(1,&wsaEvent,FALSE,10000,FALSE); 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 // 成功 -- FD_ACCEPT事件发生 { ::WSANETWORKEVENTS wsaNetEvent; ::WSAEnumNetworkEvents(sListen,wsaEvent,&wsaNetEvent); if(wsaNetEvent.lNetworkEvents&FD_ACCEPT) { if(wsaNetEvent.iErrorCode[FD_ACCEPT_BIT]==0) { 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"); } } } //::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; }