從CSDN的一個xd那裡看到這個代碼,覺得對WSAEventSelect模型的多線程實現做的非常好,程式碼文風格和代碼品質都是非常不錯。我做了些小的改動學習之用。如果你也感興趣不妨一起留言討論討論。
// WSAEventSelect_MT.cpp : Defines the entry point for the console application.<br />//</p><p>#include "stdafx.h"</p><p>#include <Winsock2.h><br />#include <malloc.h><br />#pragma comment(lib,"Ws2_32.lib")</p><p>/*<br />WSAEventSelect IO模型伺服器實現<br />-----------------------------------------<br />1.多線程實現,以支援更多的用戶端串連<br />2.多線程共用同一個監聽通訊端socketListen<br />3.IO商務邏輯模組單獨實現<br />4.各個線程各自管理自己的socket客戶串連<br />*/</p><p>const int MAX_SOCKET_COUNT_PER_THREAD = WSA_MAXIMUM_WAIT_EVENTS - 1; // 定義每個線程最大管理的socket串連數量</p><p>// 初始化socket<br />void InitSock()<br />{<br />WORD wVersionRequested;<br />WSADATA wsaData;<br />int err;</p><p>wVersionRequested = MAKEWORD( 2, 2 );</p><p>err = WSAStartup( wVersionRequested, &wsaData );<br />if ( err != 0 ) {<br />/* Tell the user that we could not find a usable */<br />/* WinSock DLL. */<br />return;<br />}</p><p>/* Confirm that the WinSock DLL supports 2.2. */<br />/* Note that if the DLL supports versions greater */<br />/* than 2.2 in addition to 2.2, it will still return */<br />/* 2.2 in wVersion since that is the version we */<br />/* requested. */<br />if ( LOBYTE( wsaData.wVersion ) != 2 ||<br />HIBYTE( wsaData.wVersion ) != 2 ) {<br />/* Tell the user that we could not find a usable */<br />/* WinSock DLL. */<br />WSACleanup( );<br />return;<br />}<br />}</p><p>//通訊端對象 -- 客戶串連通訊端資訊<br />/*<br />一個通訊端對象相關的資訊:通訊端、該通訊端相關聯的事件對象、通訊端地址<br />*/<br />typedef struct ST_SOCKET_OBJ<br />{<br />SOCKET s;//通訊端控制代碼<br />HANDLE hEvent;//與通訊端s相關聯的事件物件控點<br />sockaddr_in addrRemote;//用戶端地址資訊<br />ST_SOCKET_OBJ *pNext;//指向一個SOCKET_OBJ<br />}SOCKET_OBJ,*pSOCKET_OBJ;</p><p>//線程對象<br />/*<br />線程參數:背景工作執行緒監視的socket對象列表以及每個通訊端關聯的事件數目組<br />*/<br />typedef struct ST_THREAD_OBJ<br />{<br />HANDLE hEvents[WSA_MAXIMUM_WAIT_EVENTS];//記錄當前線程要等待的事件對象的控制代碼<br />int nSocketCount;//記錄當前線程處理的通訊端數量<br />pSOCKET_OBJ pSockHeader;//當前線程處理的通訊端對象列表的頭指標<br />pSOCKET_OBJ pSockTail;//當前線程處理的通訊端對象列表的尾指標<br />CRITICAL_SECTION cs;//關鍵程式碼片段變數,為的是同步對本結構的訪問<br />ST_THREAD_OBJ* pNext;//指向下一個THREAD_OBJ<br />}THREAD_OBJ,*pTHREAD_OBJ;</p><p>//申請一個通訊端對象 - 為一個串連的socket申請一個ST_SOCKET_OBJ對象,初始化<br />pSOCKET_OBJ GetSocketObj(SOCKET s)<br />{<br />pSOCKET_OBJ pSocket= (pSOCKET_OBJ)malloc(sizeof(SOCKET_OBJ)); // new SOCKET_OBJ()<br />if(pSocket!=NULL)<br />{<br />pSocket->hEvent=::WSACreateEvent();// 建立事件<br />pSocket->s=s;// 初始化通訊端socket<br />pSocket->pNext=NULL;<br />}<br />return pSocket;<br />}</p><p>//釋放一個通訊端對象<br />void FreeSocketObj(pSOCKET_OBJ pSocket)<br />{<br />::CloseHandle(pSocket->hEvent);<br />if(pSocket->s!=INVALID_SOCKET)<br />{<br />::closesocket(pSocket->s);<br />}<br />free(pSocket); // delete pSocket;<br />pSocket=NULL;<br />}</p><p>pTHREAD_OBJ g_pThreadList=NULL;//指向線程對象列表的表頭 -- 線程參數列表,和線程一一對應<br />CRITICAL_SECTION g_cs;//同步對全域變數g_pThreadList的訪問</p><p>//申請一個線程對象,初始化它的成員,並將它添加到線程對象列表中<br />pTHREAD_OBJ GetThreadObj()<br />{<br />pTHREAD_OBJ pThread=(pTHREAD_OBJ)malloc(sizeof(THREAD_OBJ));<br />if(pThread!=NULL)<br />{<br />pThread->pNext=NULL;<br />pThread->nSocketCount=0;<br />pThread->pSockHeader=pThread->pSockTail=NULL;<br />::InitializeCriticalSection(&pThread->cs);</p><p>//建立一個事件對象,用於指示該線程的控制代碼數組hEvents[]需要重組<br />pThread->hEvents[0]=WSACreateEvent();</p><p>//將線程對象加入線程對象列表中<br />::EnterCriticalSection(&g_cs);<br />pThread->pNext=g_pThreadList;<br />g_pThreadList=pThread;<br />::LeaveCriticalSection(&g_cs);<br />}<br />return pThread;<br />}</p><p>//釋放一個線程對象<br />void FreeThreadObj(pTHREAD_OBJ pThread)<br />{<br />::EnterCriticalSection(&g_cs);<br />pTHREAD_OBJ p=g_pThreadList;<br />if(p==pThread)<br />{<br />g_pThreadList=pThread->pNext;<br />}<br />else<br />{<br />while(p!=NULL&&p->pNext!=pThread) //找到pThread的前一個節點<br />{<br />p=p->pNext;<br />}<br />if(p!=NULL)<br />{<br />p->pNext=pThread->pNext;<br />}<br />}<br />::LeaveCriticalSection(&g_cs);<br />//釋放資源<br />::CloseHandle(pThread->hEvents[0]); // pThread->hEvents[0]為用來指示hEvents[]需要重組的事件<br />::DeleteCriticalSection(&pThread->cs);<br />::free(pThread);<br />}</p><p>//重建立立線程對象的events數組 即將pSocket中的event與pThread中的events關聯起來<br />void RebuildArray(pTHREAD_OBJ pThread)<br />{<br />::EnterCriticalSection(&pThread->cs);<br />pSOCKET_OBJ pSocket=pThread->pSockHeader;<br />int n=1; //從下標為1開始 跳過pThread->hEvents[0]<br />while(pSocket!=NULL)<br />{<br />pThread->hEvents[n++]=pSocket->hEvent;<br />pSocket=pSocket->pNext;<br />}<br />::LeaveCriticalSection(&pThread->cs);<br />}</p><p>LONG g_nTotalConnections;//總共串連數量<br />LONG g_nCurrentConnections;//當前串連數量</p><p>//向線程的通訊端列表中插入一個通訊端對象<br />BOOL InsertSocketObj(pTHREAD_OBJ pThread,pSOCKET_OBJ pSocket)<br />{<br />BOOL bRet=FALSE;<br />::EnterCriticalSection(&pThread->cs);<br />if(pThread->nSocketCount<MAX_SOCKET_COUNT_PER_THREAD) // 每個線程最多管理MAX_SOCKET_COUNT_PER_THREAD個socket串連<br />{<br />if(pThread->pSockHeader==NULL)<br />{// 對象列表為空白<br />pThread->pSockHeader=pThread->pSockTail=pSocket;<br />}<br />else<br />{// 追加一個到對象列表末尾<br />pThread->pSockTail->pNext=pSocket;<br />pThread->pSockTail=pSocket;<br />}<br />pThread->nSocketCount++;<br />bRet=TRUE;<br />}<br />::LeaveCriticalSection(&pThread->cs);<br />if(bRet)<br />{<br />InterlockedIncrement(&g_nTotalConnections);<br />InterlockedIncrement(&g_nCurrentConnections);<br />}<br />return bRet;<br />}</p><p>//從給定線程的通訊端對象列表中移除一個通訊端對象<br />void RemoveSocketObj(pTHREAD_OBJ pThread,pSOCKET_OBJ pSocket)<br />{<br />::EnterCriticalSection(&pThread->cs);<br />//在通訊端對象列表中尋找指定的通訊端對象,找到後將它移除<br />pSOCKET_OBJ pTemp=pThread->pSockHeader;<br />if(pTemp==pSocket)<br />{<br />if(pThread->pSockHeader==pThread->pSockTail)<br />{<br />pThread->pSockHeader=pThread->pSockTail=pTemp->pNext;<br />}<br />else<br />{<br />pThread->pSockHeader=pTemp->pNext;<br />}<br />}<br />else<br />{<br />while(pTemp!=NULL&&pTemp->pNext!=pSocket)<br />{<br />pTemp=pTemp->pNext;<br />}<br />if(pTemp!=NULL)<br />{<br />if(pThread->pSockTail==pSocket)<br />{<br />pThread->pSockTail=pTemp;<br />}<br />pTemp->pNext=pSocket->pNext;<br />}<br />}<br />pThread->nSocketCount--;<br />::LeaveCriticalSection(&pThread->cs);<br />::WSASetEvent(pThread->hEvents[0]); //指示線程重建控制代碼數組<br />::InterlockedDecrement(&g_nCurrentConnections);<br />}</p><p>pSOCKET_OBJ FindSocketObj(pTHREAD_OBJ pThread,int nIndex)<br />{<br />pSOCKET_OBJ pSocket=pThread->pSockHeader;<br />while(--nIndex)<br />{<br />if(pSocket==NULL)<br />{<br />return NULL;<br />}<br />pSocket=pSocket->pNext;<br />}<br />return pSocket;<br />}</p><p>BOOL HandleIO(pTHREAD_OBJ pThread,pSOCKET_OBJ pSocket)<br />{<br />::WSANETWORKEVENTS wsaEvent;<br />::WSAEnumNetworkEvents(pSocket->s,pSocket->hEvent/*待覆位控制代碼*/,&wsaEvent);<br />// FD_READ事件<br />if(wsaEvent.lNetworkEvents&FD_READ)<br />{<br />if(wsaEvent.iErrorCode[FD_READ_BIT]==0)<br />{<br />char *buf=new char[1024];<br />__try<br />{</p><p>int nRec=recv(pSocket->s,buf,1024,0);<br />if(nRec>0)<br />{<br />buf[nRec]='/0';<br />printf("收到資料:%s/n",buf);<br />return true;<br />}<br />else<br />{<br />return false;<br />}<br />}<br />__finally<br />{<br />delete[]buf;<br />}<br />}<br />}<br />// FD_CLOSE事件<br />else if(wsaEvent.lNetworkEvents&FD_CLOSE)<br />{<br />printf("一串連繼開/n");<br />RemoveSocketObj(pThread,pSocket); // 需要重建控制代碼數組<br />//::WSASetEvent(pThread->hEvents[0]); //指示線程重建控制代碼數組<br />FreeSocketObj(pSocket);<br />return false;<br />}<br />return true;<br />}</p><p>//處理I/O的線程<br />DWORD WINAPI ServerThread(LPVOID lpParam)<br />{<br />pTHREAD_OBJ pThread=(pTHREAD_OBJ)lpParam;<br />printf("新線程%d起動/n",GetCurrentThreadId());<br />while(true)<br />{<br />//等待網路事件<br />int nIndex=WSAWaitForMultipleEvents(pThread->nSocketCount+1,pThread->hEvents,false,WSA_INFINITE,false);<br />nIndex=nIndex-WSA_WAIT_EVENT_0;<br />//if(WSA_WAIT_FAILED==nIndex||WSA_WAIT_TIMEOUT==nIndex)<br />//{// 失敗或者逾時<br />//continue;<br />//}<br />//else<br />//{// 成功受信<br />//if(0==nIndex) // pThread->hEvents[0]受信<br />//{<br />//RebuildArray(pThread); // 重建控制代碼數組<br />//if(pThread->nSocketCount==0)<br />//{// 如果該線程串連的socket數量已經為0,說明所有的客戶都已經中斷連線則線程函數自動結束<br />//FreeThreadObj(pThread); // 線程退出時候回收線程參數分配的記憶體<br />//printf("線程%d退出/n",GetCurrentThreadId());<br />//return 0;<br />//}<br />//::WSAResetEvent(pThread->hEvents[0]);<br />//}<br />//else<br />//{<br />//pSOCKET_OBJ pSocket=FindSocketObj(pThread,nIndex);<br />//if(pSocket!=NULL)<br />//{<br />//if(!HandleIO(pThread,pSocket))<br />//{<br />//RebuildArray(pThread); // 重建控制代碼數組<br />//}<br />//}<br />//else<br />//{<br />//printf("unable to find socket object/n");<br />//}<br />//}<br />//}<br />//查看受信事件<br />for(int i=nIndex;i<pThread->nSocketCount+1;i++)<br />{<br />nIndex=WSAWaitForMultipleEvents(1,&pThread->hEvents[i],true,0,false);<br />if(WSA_WAIT_FAILED==nIndex||WSA_WAIT_TIMEOUT==nIndex)<br />{<br />continue;<br />}<br />else<br />{<br />if(0==i)<br />{<br />RebuildArray(pThread); // 重建控制代碼數組<br />if(pThread->nSocketCount==0)<br />{<br />FreeThreadObj(pThread);<br />printf("線程%d退出/n",GetCurrentThreadId());<br />return 0;<br />}<br />::WSAResetEvent(pThread->hEvents[0]);<br />}<br />else<br />{<br />pSOCKET_OBJ pSocket=FindSocketObj(pThread,i);<br />if(pSocket!=NULL)<br />{<br />if(!HandleIO(pThread,pSocket))<br />{<br />RebuildArray(pThread); // 重建控制代碼數組<br />}<br />}<br />else<br />{<br />printf("unable to find socket object/n");<br />}<br />}<br />}<br />}<br />} // end while()<br />}</p><p>//將一個通訊端對象安排給閒置線程處理<br />//如果所有線程接受的socket已滿,則建立線程提供服務<br />void AssignToFreeThread(pSOCKET_OBJ pSocket)<br />{<br />pSocket->pNext=NULL;<br />::EnterCriticalSection(&g_cs);<br />pTHREAD_OBJ pThread=g_pThreadList;<br />//試圖插入到現存的線程<br />while(pThread!=NULL)<br />{<br />if(InsertSocketObj(pThread,pSocket))<br />{<br />break;<br />}<br />pThread=pThread->pNext;<br />}<br />//如果沒有空閑線程,為這個通訊端對象建立新的線程<br />if(pThread==NULL)<br />{<br />pThread=GetThreadObj();<br />InsertSocketObj(pThread,pSocket);<br />::CreateThread(NULL,0,ServerThread,pThread,0,NULL);<br />}<br />::LeaveCriticalSection(&g_cs);<br />//指示線程重建控制代碼數組<br />::WSASetEvent(pThread->hEvents[0]);<br />}</p><p>int _tmain(int argc, _TCHAR* argv[])<br />{<br />InitSock();<br />SOCKET sListen=socket(AF_INET,SOCK_STREAM,0);<br />sockaddr_in sin;<br />sin.sin_addr.S_un.S_addr=INADDR_ANY;<br />sin.sin_family=AF_INET;<br />sin.sin_port=htons(3456);<br />if(SOCKET_ERROR==bind(sListen,(sockaddr*)&sin,sizeof(sockaddr)))<br />{<br />printf("綁定失敗/n");<br />::WSACleanup();<br />return 0;<br />}<br />listen(sListen,SOMAXCONN);<br />::WSAEVENT wsaEvent=::WSACreateEvent();<br />::WSAEventSelect(sListen,wsaEvent,FD_ACCEPT);<br />::InitializeCriticalSection(&g_cs);<br />while(true)<br />{<br />//int nRet=::WaitForSingleObject(wsaEvent,10000);<br />int nRet=::WSAWaitForMultipleEvents(1,&wsaEvent,FALSE,10000,FALSE);<br />if(nRet==WAIT_FAILED) // 失敗<br />{<br />printf("failed wait for single object/n");<br />break;<br />}<br />else if(nRet==WSA_WAIT_TIMEOUT) // 逾時<br />{<br />printf(" TotalConnections:%d/n",g_nTotalConnections);<br />printf(" TotalCurrentConnections:%d/n",g_nCurrentConnections);<br />}<br />else // 成功 -- FD_ACCEPT事件發生<br />{<br />::WSANETWORKEVENTS wsaNetEvent;<br />::WSAEnumNetworkEvents(sListen,wsaEvent,&wsaNetEvent);<br />if(wsaNetEvent.lNetworkEvents&FD_ACCEPT)<br />{<br />if(wsaNetEvent.iErrorCode[FD_ACCEPT_BIT]==0)<br />{<br />while(true)<br />{// 把等待串連隊列當中的使用者都接受串連建立請求,建立串連後派遣給相關的服務線程處理<br />sockaddr_in si;<br />int nLen=sizeof(si);<br />SOCKET client=accept(sListen,(sockaddr*)&si,&nLen);<br />if(client==SOCKET_ERROR)<br />break; // 沒有串連請求需要建立了<br />pSOCKET_OBJ pSocket=GetSocketObj(client);<br />pSocket->addrRemote=si;<br />::WSAEventSelect(pSocket->s,pSocket->hEvent,FD_READ|FD_CLOSE);<br />AssignToFreeThread(pSocket);<br />printf("一新串連/n");<br />}<br />}<br />}<br />//::ResetEvent(wsaEvent);<br />//while(true)<br />//{// 把等待串連隊列當中的使用者都接受串連建立請求,建立串連後派遣給相關的服務線程處理<br />//sockaddr_in si;<br />//int nLen=sizeof(si);<br />//SOCKET client=accept(sListen,(sockaddr*)&si,&nLen);<br />//if(client==SOCKET_ERROR)<br />//break; // 沒有串連請求需要建立了<br />//pSOCKET_OBJ pSocket=GetSocketObj(client);<br />//pSocket->addrRemote=si;<br />//::WSAEventSelect(pSocket->s,pSocket->hEvent,FD_READ|FD_CLOSE);<br />//AssignToFreeThread(pSocket);<br />//printf("一新串連/n");<br />//}<br />}<br />}<br />::DeleteCriticalSection(&g_cs);<br />return 0;<br />}