开启服务和停止服务
Start函数用于开启服务
1 初始化状态变量
2 创建监听套接字
3 加载使用扩展API函数
4 创建完成端口对象
5 建立监听套接字和完成端口对象间的关联
6 为监听套接字注册FD_ACCEPT时间
7 投递AcceptEx IO不够时可以得到通知后创建监听线程
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,请求来接收这个连接时,就会触发该时间对象。
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; }