Start函數用於開啓服務數組
1 初始化狀態變量服務器
2 建立監聽套接字socket
3 加載使用擴展API函數函數
4 建立完成端口對象post
5 創建監聽套接字和完成端口對象間的關聯ui
6 爲監聽套接字註冊FD_ACCEPT時間this
7 投遞AcceptEx IO不夠時能夠獲得通知後建立監聽線程spa
BOOL CIOCOPServer::Start(int nPort,int nMaxConnnections,int nMaxFreeBuffers,int nMaxFreeContexts,int nInitialReads) { //檢查服務是否啓動 if(m_bServerStarted) return FALSE; //保存參數 m_nPort = nPort; m_nMaxConnnections = nMaxConnnections; m_nMaxFreeBuffers = nMaxFreeBuffers; m_nMaxFreeContexts = nMaxFreeContexts; m_nInitialReads = nInitialReads; //初始化變量 m_bServerStarted = TRUE; m_bShutDown = FALSE; //建立監聽套接字,綁定到本地端口, 進入監聽模式 m_sListen = ::WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); SOCKADDR_IN si; si.sin_family = AF_INET; si.sin_port = nPort; si.sin_addr.S_un.S_addr = INADDR_ANY; if(::bind(m_sListen,(sockaddr*)&si,sizeof(si))==SOCKET_ERROR) { m_bServerStarted = FALSE; return FALSE; } ::listen(m_sListen,200); //建立完成端口 m_hConnection = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0); //加載擴展函數AcceptEx GUID GuidAcceptEx = WSAID_ACCEPTEX; DWORD dwBytes; WSAIotcl( m_sListen, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &m_lpfnAcceptEx, sizeof(m_lpfnAcceptEx), &dwBytes, NULL, NULL ); //加載GetAcceptExSockaddrs GUID GuidAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS; ::WSAIoctl(m_sListen, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuiGetAcceptExSockaddrs, sizeof(GuidGetAcceptExSockaddrs), &m_lpfnAcceptExSockaddrs, sizeof(m_lofnAcceptExSockaddrs), &dwBytes, NULL, NULL); //將監聽套接字關聯到完成端口 ::CreateIoCompletionPort((HANDLE)m_sListen,m_hConnection,(DWORD)0,0); //註冊FD_ACCEPT事件 WSAEventSelect(m_sListen,m_hAcceptEvent,FD_ACCEPT); //建立監聽線程 m_hListenThread = ::CreateThread(NULL,0,_ListenThreadProc,this,0,NULL); return TRUE; }
監聽線程_ListenThreadProc主要責任:監聽套接字投遞AcceptEx IO請求。線程
m_hAcceptEvent:當winsock接收到新的鏈接請求,可是AcceptEx IO,請求來接收這個鏈接時,就會觸發該時間對象。code
m_hRepostEvent:與IO進行交互。
_ListenThreadProc在下面3中狀況下投遞Accept請求:
1 程序初始化,要先投遞幾個Accept請求,個數由用戶指定
2 處理IO的線程接受到一個客戶,使m_hRepostEvent時間受信,_ListenThreadProc線程獲得通知後再投遞一個Accept請求。
3 程序運行期間,若是投遞的Accept請求不夠用,用戶的鏈接請求未可以立刻處理,這時候再投遞若干個Accept請求。
DWORD WINAPI CIOCPServer::_ListenThreadProc(LPVOID lpParam) { CIOCPServer *pThis = (CIOCPServer*)lpParam; //在監聽套接字上投遞幾個AcceptIO CIOCPBuffer *pBuffer; for(int i=0;i<pThis->m_nInitialAccepts;i++) { pBuffer = pThis->AllocateBuffer(BUFFER_SIZE); if(pBuffer==NULL) return -1; pTHis->InsertPendingAccept(pBuffer); pThis->PostAccept(pBuffer); } //構建事件對象數組 HANDLE hWaitEvents[2+MAX_THREAD]; int nEventCount = 0; hWaitEvents[nEventCount++]=pThis->m_hAcceptEvent; hWaitEvents[nEventCount++]=pThis->m_hRepostEvent; //建立指定數量的工做線程在完成端口上處理IO for(i=0;i<MAX_THREAD;i++) { hWaitEvents[nEventCount++]=::CreateThread(NULL,0,_WorkerThreadProc,pThis,0,NULL); } //下面進入無限循環,處理時間對象數組中的事件 while(TRUE) { int nIndex = ::WSAWaitForMultipleEvents(nEventCount,hWaitEvents,FALSE,60*1000,FALSE); //檢查是否要中止服務 if(pThis->m_bShutDown || nIndex==WSA_WAIT_FAILED) { //關閉全部鏈接 pThis->CloseAllConnections(); ::Sleep(0); //關閉監聽套接字 ::closesocket(pThis->m_sListen); pThis->m_sListen=INVALID_SOCKET; ::Sleep(0); //通知全部IO處理線程退出 for(int i=2;i<MAX_THREAD+2;i++) { ::PostQueuedCompletionStatus(pThis->m_hCompletion,-1,0,NULL); } //等待IO處理線程退出 ::WaitForMultipleObjects(MAX_THREAD,&hWaitEvents[2],TRUE,5*1000); for(i=2;i<MAX_THREAD+2;i++) { ::CloseHandle(hWaitEvents[i]); } ::CloseHandle(pThis->m_hCompletion); pThis->FreeBuffers(); pThis->FreeContexts(); ::ExitThread(0); } //定時檢查全部未返回的AcceptEx IO的鏈接創建多長時間 if(nIndex == WSA_WAIT_TIMEOUT) { pBuffer = pThis->m_pPendingAccepts; while(pBuffer!=NULL) { int nSeconds; int nLen = sizeof(nSeconds); //取得鏈接創建時間 ::getsockopt(pBuffer->sClient,SOL_SOCKET,SO_CONNECT_TIME,(char*)&nSeconds,&nLen); //若是超過兩分鐘,就丟棄 if(nSeconds!=-1 && nSeconds>=2*60) { closesocket(pBuffer->sClient); pBuffer->sClient = INVALIDE_SOCKET; } pBuffer = pBuffer->pNext; } } else { nIndex = nIndex-WAIT_OBJECT_0; WSANETWORKEVENTS ne; int nLimit=0; if(nIndex==0)//m_hAcceptEvent時間對象受信,說明投遞的Accept請求不夠,須要增長 { ::WSAEnumNetworkEvents(pThis->m_sListen,hWaitEvents[nIndex],&ne); if(ne.lNetworkEvents & FD_ACCEPT) { nLimit = 50; } } else if(nIndex==1)//m_hRepostEvent事件對象受信,說明處理IO的線程接受到新的客戶 { nLimit = InterlockedExchange(&pThis->m_nRepostCount,0); } else if(nIndex>1)//IO服務線程退出,說明有錯誤發生,關閉服務器 { pThis->m_bShutDown = TRUE; continue; } //投遞nLimit個AcceptEx IO 請求 int i=0; while(i++ < nLimit && pThis->m_nPendingAcceptCount < pThis->m_nMaxAccepts) { pBuffer = pThis->AllocateBuffer(BUFFER_SIZE); if(pBuffer!=NULL) { pThis->InsertPendingAccept(pBuffer); pThis->PostAccept(pBuffer); } } } } return 0; }
3 中止服務函數ShutDown
void CIOCPServer::ShutDown() { if(!m_bServerStarted) return; //通知監聽線程,立刻中止服務 m_bShutDown = TRUE; ::SetEvent(m_hAcceptEvent); //等待監聽線程退出 ::WaitForSingleObject(m_hListenThread,INFINITE); ::CloseHandle(m_hListenThread); m_hListenThread = NUll; m_bServerStarted = FALSE; }